Java中的synchronized有幾種加鎖方式(實例詳解)
在Java中,synchronized關鍵字提供了內置的支持來實現同步訪問共享資源,以避免并發(fā)問題。synchronized主要有三種加鎖方式:
1.同步實例方法
當一個實例方法被聲明為synchronized時,該方法將同一時間只能被一個線程訪問。鎖是當前對象實例(即this)。
public class SynchronizedInstanceMethod {
public synchronized void doSomething() {
// 同步代碼塊
// 同一時間只能有一個線程執(zhí)行這里的代碼
}
public static void main(String[] args) {
SynchronizedInstanceMethod obj = new SynchronizedInstanceMethod();
// 創(chuàng)建多個線程訪問obj的doSomething方法,它們將串行執(zhí)行
// ...
}
}當我們創(chuàng)建了兩個線程來并發(fā)地增加計數器,由于我們使用了synchronized,因此計數器的增加線程是安全的,即使兩個線程都在嘗試修改同一個共享變量。在同步實例方法中,鎖分別是實例對象和類對象。
public class SynchronizedInstanceMethodExample {
private int count = 0;
// 同步實例方法,鎖定的是當前對象的實例(this)
public synchronized void increment() {
count++;
System.out.println(Thread.currentThread().getName() + " incremented count to " + count);
}
public static void main(String[] args) {
final SynchronizedInstanceMethodExample example = new SynchronizedInstanceMethodExample();
// 創(chuàng)建兩個線程來增加計數器
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
}, "Thread-1");
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
}, "Thread-2");
// 啟動線程
thread1.start();
thread2.start();
// 等待線程完成
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 輸出最終計數
System.out.println("Final count: " + example.count);
}
}2.同步靜態(tài)方法
當一個靜態(tài)方法被聲明為synchronized時,該方法將同一時間只能被一個線程訪問。鎖是Class對象,而不是實例對象。
public class SynchronizedStaticMethod {
public static synchronized void doSomethingStatic() {
// 同步代碼塊
// 同一時間只能有一個線程執(zhí)行這里的代碼
}
public static void main(String[] args) {
// 創(chuàng)建多個線程訪問SynchronizedStaticMethod的doSomethingStatic方法,它們將串行執(zhí)行
// ...
}
}當我們創(chuàng)建了兩個線程來并發(fā)地增加計數器,由于我們使用了synchronized,因此計數器的增加線程是安全的,即使兩個線程都在嘗試修改同一個共享變量。在同步靜態(tài)方法中,鎖分別也是實例對象和類對象。
public class SynchronizedStaticMethodExample {
private static int count = 0;
// 同步靜態(tài)方法,鎖定的是當前對象的類(Class)對象
public static synchronized void increment() {
count++;
System.out.println(Thread.currentThread().getName() + " incremented static count to " + count);
}
public static void main(String[] args) {
// 創(chuàng)建兩個線程來增加計數器
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
increment();
}
}, "Thread-1");
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
increment();
}
}, "Thread-2");
// 啟動線程...
// 等待線程完成...
// 輸出最終計數...
// (與上面的例子類似,但省略了重復的代碼)
}
}3.同步代碼塊
我們可以使用synchronized關鍵字來定義一個代碼塊,而不是整個方法。在這種情況下,你可以指定要獲取的鎖對象。這提供了更細粒度的同步控制。
public class SynchronizedBlock {
private final Object lock = new Object(); // 用于同步的鎖對象
public void doSomething() {
synchronized (lock) {
// 同步代碼塊
// 同一時間只有一個線程能夠執(zhí)行這里的代碼
}
// 這里的代碼不受同步代碼塊的約束
}
public static void main(String[] args) {
SynchronizedBlock obj = new SynchronizedBlock();
// 創(chuàng)建多個線程訪問obj的doSomething方法,但只有在synchronized塊中的代碼將串行執(zhí)行
// ...
}
}在上面的SynchronizedBlock類中,我們創(chuàng)建了一個私有的Object實例lock作為鎖對象。當線程進入synchronized (lock)塊時,它會嘗試獲取lock對象的鎖。如果鎖已經被其他線程持有,那么該線程將被阻塞,直到鎖被釋放。
當我們創(chuàng)建了兩個線程來并發(fā)地增加計數器,同步代碼塊的例子中,我們顯式地指定了一個對象作為鎖。
public class SynchronizedBlockExample {
private final Object lock = new Object(); // 用于同步的鎖對象
private int count = 0;
// 同步代碼塊,指定了鎖對象
public void increment() {
synchronized (lock) {
count++;
System.out.println(Thread.currentThread().getName() + " incremented count to " + count);
}
}
public static void main(String[] args) {
// 類似于上面的例子,但使用SynchronizedBlockExample的increment方法
// ...(省略了重復的代碼)
}
}注意:使用synchronized時應該盡量避免在持有鎖的情況下執(zhí)行耗時的操作,因為這會導致其他等待鎖的線程長時間阻塞。同時,過度使用synchronized可能會導致性能下降,因為它會引入線程間的競爭和可能的上下文切換。在設計并發(fā)程序時,應該仔細考慮同步的粒度,并可能使用其他并發(fā)工具(如ReentrantLock、Semaphore、CountDownLatch等)來提供更細粒度的控制。
到此這篇關于java的synchronized有幾種加鎖方式的文章就介紹到這了,更多相關java synchronized加鎖內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

