[英]Why is synchronized block better than synchronized method?
我已經開始學習線程同步。
同步方法:
public class Counter {
private static int count = 0;
public static synchronized int getCount() {
return count;
}
public synchronized setCount(int count) {
this.count = count;
}
}
同步塊:
public class Singleton {
private static volatile Singleton _instance;
public static Singleton getInstance() {
if (_instance == null) {
synchronized(Singleton.class) {
if (_instance == null)
_instance = new Singleton();
}
}
return _instance;
}
}
什么時候應該使用synchronized
方法和synchronized
塊?
為什么synchronized
塊比synchronized
方法更好?
這不是更好的問題,只是不同。
當您同步一個方法時,您實際上是在同步到對象本身。 在靜態方法的情況下,您正在同步到對象的類。 所以下面兩段代碼的執行方式是一樣的:
public synchronized int getCount() {
// ...
}
這就像你寫的一樣。
public int getCount() {
synchronized (this) {
// ...
}
}
如果要控制與特定對象的同步,或者只想將方法的一部分同步到該對象,則指定一個synchronized
塊。 如果在方法聲明中使用了synchronized
關鍵字,它會將整個方法同步到對象或類。
雖然通常不是一個問題,但從安全角度來看,最好在私有對象上使用同步,而不是將它放在方法上。
將它放在方法上意味着您正在使用對象本身的鎖來提供線程安全。 通過這種機制,您代碼的惡意用戶也有可能獲得您對象的鎖,並永久持有它,從而有效地阻塞其他線程。 非惡意用戶可以在不經意間有效地做同樣的事情。
如果您使用私有數據成員的鎖,則可以防止這種情況發生,因為惡意用戶不可能獲得您私有對象的鎖。
private final Object lockObject = new Object();
public void getCount() {
synchronized( lockObject ) {
...
}
}
Bloch 的 Effective Java(第 2 版)第 70 項中提到了這種技術
不同之處在於獲取的是哪個鎖:
同步方法獲取整個對象的鎖。 這意味着當一個線程正在運行該方法時,沒有其他線程可以在整個對象中使用任何同步方法。
synchronized 塊在synchronized 關鍵字之后的括號之間獲取對象中的鎖。 這意味着在同步塊退出之前,沒有其他線程可以獲取鎖定對象的鎖定。
因此,如果您想鎖定整個對象,請使用同步方法。 如果您想讓其他線程可以訪問對象的其他部分,請使用同步塊。
如果仔細選擇鎖定的對象,同步塊將導致較少的爭用,因為整個對象/類都沒有被阻塞。
這同樣適用於靜態方法:同步靜態方法將獲取整個類對象中的鎖,而靜態方法內的同步塊將獲取括號之間對象中的鎖。
同步塊和同步方法的區別如下:
同步塊: synchronized(this){}
同步方法: public synchronized void fun(){}
定義“更好”。 同步塊只是更好,因為它允許您:
現在,您的具體示例是可疑的雙重檢查鎖定模式的示例(在較舊的 Java 版本中,它已損壞,並且很容易出錯)。
如果您的初始化成本較低,最好立即使用 final 字段進行初始化,而不是在第一次請求時進行初始化,這也將消除同步的需要。
僅當您希望您的類是線程安全的時才應使用同步。 事實上,大多數類無論如何都不應該使用同步。 同步方法只會在此對象上提供鎖定,並且僅在其執行期間提供鎖定。 如果你真的想讓你的類線程安全,你應該考慮讓你的變量可變或同步訪問。
使用同步方法的問題之一是該類的所有成員都將使用相同的鎖,這會使您的程序變慢。 在您的情況下,同步方法和塊將執行沒有什么不同。 我會推薦的是使用專用鎖並使用類似這樣的同步塊。
public class AClass {
private int x;
private final Object lock = new Object(); //it must be final!
public void setX() {
synchronized(lock) {
x++;
}
}
}
在你的情況下,兩者都是等價的!
同步一個靜態方法相當於在相應的 Class 對象上同步一個塊。
實際上當你聲明一個synchronized靜態方法時,是在Class對象對應的monitor上獲得鎖的。
public static synchronized int getCount() {
// ...
}
與
public int getCount() {
synchronized (ClassName.class) {
// ...
}
}
因為鎖是昂貴的,當你使用同步塊時,你只在_instance == null
鎖定,並且在_instance
最終初始化之后你將永遠不會鎖定。 但是當您同步方法時,您會無條件鎖定,即使在_instance
初始化之后也是如此。 這是雙重檢查鎖定優化模式http://en.wikipedia.org/wiki/Double-checked_locking背后的想法。
不應將其視為最佳使用問題,但它確實取決於用例或場景。
同步方法
可以將整個方法標記為同步,從而導致對 this 引用(實例方法)或類(靜態方法)的隱式鎖定。 這是實現同步的非常方便的機制。
步驟線程訪問同步方法。 它隱式地獲取鎖並執行代碼。 如果其他線程要訪問上述方法,則必須等待。 線程無法獲得鎖,將被阻塞,必須等到鎖被釋放。
同步塊
要為一組特定的代碼塊獲取對象上的鎖,同步塊是最合適的。 由於一個塊就足夠了,使用同步方法將是一種浪費。
更具體地說,使用 Synchronized Block ,可以定義要獲取鎖的對象引用。
Synchronized 塊和 Synchronized 方法之間的一個經典區別是 Synchronized 方法鎖定整個對象。 同步塊只是鎖定塊內的代碼。
同步方法:基本上這兩種同步方法禁用多線程。 所以一個線程完成method1(),另一個線程等待Thread1 完成。
類 SyncExerciseWithSyncMethod {
public synchronized void method1() {
try {
System.out.println("In Method 1");
Thread.sleep(5000);
} catch (Exception e) {
System.out.println("Catch of method 1");
} finally {
System.out.println("Finally of method 1");
}
}
public synchronized void method2() {
try {
for (int i = 1; i < 10; i++) {
System.out.println("Method 2 " + i);
Thread.sleep(1000);
}
} catch (Exception e) {
System.out.println("Catch of method 2");
} finally {
System.out.println("Finally of method 2");
}
}
}
在方法 1 中
方法一的最后
方法 2 1
方法 2 2
方法 2 3
方法 2 4
方法 2 5
方法 2 6
方法 2 7
方法 2 8
方法 2 9
方法二的最后
同步塊:允許多個線程同時訪問同一個對象【啟用多線程】。
類 SyncExerciseWithSyncBlock {
public Object lock1 = new Object();
public Object lock2 = new Object();
public void method1() {
synchronized (lock1) {
try {
System.out.println("In Method 1");
Thread.sleep(5000);
} catch (Exception e) {
System.out.println("Catch of method 1");
} finally {
System.out.println("Finally of method 1");
}
}
}
public void method2() {
synchronized (lock2) {
try {
for (int i = 1; i < 10; i++) {
System.out.println("Method 2 " + i);
Thread.sleep(1000);
}
} catch (Exception e) {
System.out.println("Catch of method 2");
} finally {
System.out.println("Finally of method 2");
}
}
}
}
在方法 1 中
方法 2 1
方法 2 2
方法 2 3
方法 2 4
方法 2 5
方法一的最后
方法 2 6
方法 2 7
方法 2 8
方法 2 9
方法二的最后
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.