[英]When should I use a synchronized method and when a synchronized block in Java?
[英]Should I try to avoid static synchronized method
根據我的理解,以下代碼效率不高:
class Foo {
static Resource resource1;
static Resource resource2;
static synchronized void methodA() {
resource1.add("abc");
}
static synchronized void methodB() {
resource2.add("abc");
}
}
根據我的理解,兩種方法都鎖定在一個對象(類對象Foo.class
)中,所以我猜測以下是一個很好的優化?
class Foo {
static Resource resource1;
static Resource resource2;
static void methodA() {
synchronized(resource1) {
resource1.add("abc");
}
}
static void methodB() {
synchronized(resource2) {
resource2.add("123");
}
}
}
只要這兩種資源不相互依賴。
我什么時候應該考慮使用靜態同步方法?
當類抽象訪問單個關鍵資源時使用static synchronized
構造,因此鎖定類在語義上是正確的。
如果您的類抽象訪問多個關鍵資源,那么您必須使用更精細的鎖定,如您的示例所示。
您可以將方法的synchronized
修飾符視為語法糖,除了鎖定類(或方法不是靜態的實例)之外,沒有額外的黑魔法。
在您的第一個示例中,如果單個類提供對兩個不同關鍵資源的訪問,如果它們完全不相關,那么這是值得懷疑的。 也許您可以將關鍵部分移動到Resource類本身。
您的優化是正確的。
第一個代碼鎖定在Foo.class
第二個代碼鎖定兩個不同的對象: resource1
和resource2
。
從視覺上你可以想象這一點
第一個代碼:
Thread 1 Thread 2
------------------------------
Foo.methodA()
Foo.methodB()
// A call to methodB needs to wait for completion of methodA
第二個代碼:
Thread 1 Thread 2
------------------------------
Foo.methodA() Foo.methodB()
// At the same time in a machine with at least a dual core
只有在有一個資源要同步時,才應考慮使用靜態同步方法。
優化很好,但要注意可能的死鎖。 在示例中,有時您將決定訪問這兩種資源:
class Foo {
static Resource resource1;
static Resource resource2;
static void methodA() {
synchronized(resource1) {
resource1.add("abc");
synchronized(resource2) {
resource2.add("abc");
}
}
}
static void methodB() {
synchronized(resource2) {
resource2.add("123");
synchronized(resource1) {
resource1.add("123");
}
}
}
}
這可能導致死鎖:
為避免這種情況,您可以使Resource類成為線程安全的:
class Resource {
private final Object mLock = new Object();
...
public void add(String str) {
synchronized(mLock) {
//do stuff
}
}
}
第二種方法(鎖定對象)是首選,因為它可以讓您更好地控制何時應該進行鎖定。 更重要的是,它可以防止您的類的外部方無限期地鎖定您的類,從而阻止您自己的方法執行。
請考慮以下內容:想象一些外部代碼包含以下語句:
synchronized (Foo.class) {
Thread.sleep(10000);
}
現在,如果您在方法1中使用了類方法本身的synchronized,那么同時嘗試調用methodA或methodB的其他類將被阻塞,直到睡眠完成。 但是,如果您在方法2中使用內部對象的內部鎖定,那么其他類將不必等待。
由於上述原因,我不會真正推薦方法1。
PS我剛剛注意到方法2中的內部鎖沒有被聲明為final。 如果在方法忙於鎖定它們時重新分配它們將是一個問題(然后鎖將在不同的實例上)。 為了防止這種情況,請將它們聲明為final,如下所示:
final static Resource resource1 = new Resource(...);
final static Resource resource2 = new Resource(...);
簡而言之,永遠不要在非最終對象上同步。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.