2016-04-21 55 views
6

從我所瞭解下面的代碼效率不高:我應儘量避免靜態同步方法

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"); 
     } 
    } 
} 

只要兩個資源不相互依賴。

什麼時候應該考慮使用靜態同步方法?

回答

7

當你的類抽象化訪問單個critical resource時,使用static synchronized構造,因此對類的鎖定在語義上是正確的。

如果您的類抽象化訪問多個關鍵資源,那麼您必須使用更好的鎖定,如您的示例中所示。

您可以將方法的​​修飾符視爲語法糖,除了鎖定類(或者實例,如果方法不是靜態的)之外,沒有額外的黑魔法。

在第一個示例中,如果單個類提供對兩個不同關鍵資源的訪問(如果它們完全不相關),這是值得懷疑的。也許你可以將關鍵部分自己移動到資源類。

5

您的優化是正確的。

Foo.class

上兩個不同的對象的第二個密碼鎖的第一碼鎖:resource1resource2

視覺上,你可以想像這個

首先代碼:

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 

你應該考慮使用靜態同步方法只有當你有一個資源同步。

0

優化是好的,但要注意可能的死鎖。在例子中,有時你會決定訪問兩個資源:

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"); 
      } 
     } 
    } 
} 

這會導致死鎖:

  1. 線程A試圖執行了methodA();
  2. 線程B嘗試同時執行methodB();
  3. 線程A獲得資源1鎖,線程B獲得資源2鎖定
  4. 線程A試圖獲得資源2鎖定,並開始等待鎖釋放
  5. 線程B試圖獲得資源1鎖定,並開始等待鎖釋放
  6. 死鎖

爲了避免這種情況,你可以讓你的資源類是線程安全的:

class Resource { 
    private final Object mLock = new Object(); 
    ... 
    public void add(String str) { 
     synchronized(mLock) { 
      //do stuff 
     } 
    } 
} 
0

第二種方法(鎖定對象)是更可取的,因爲它可以讓您更好地控制鎖定發生的時間。更重要的是,它可以防止你的班級的外部參與者無限期地鎖定你的班級,阻止你自己的方法執行。

考慮以下幾點:試想一下,一些外部代碼包含以下語句:

synchronized (Foo.class) { 
    Thread.sleep(10000); 
} 

現在,如果你用在類的方法本身同步與方法1,其他類同時試圖調用了methodA或的methodB會被阻止直到睡眠完成。如果你在方法2中使用內部對象的內部鎖定,那麼其他類不需要等待。

由於上述原因,我不會真的建議方法1.

PS我剛剛注意到,在方法2內的鎖並沒有聲明爲final。如果在方法忙於鎖定它們時重新分配這些,這將成爲一個問題(然後鎖將處於不同的實例化中)。爲了防止出現這種情況,最好如下所示將它們聲明爲:

final static Resource resource1 = new Resource(...); 
final static Resource resource2 = new Resource(...); 

總之,從不同步非最終對象。