探討Java中static synchronized和synchronized


synchronized提供內部鎖的機制,防止其它線程同時進入synchronized的代碼塊。synchronized由兩部分組成:1.鎖對象的引用;2.鎖保護的代碼塊。

對鎖對象引用的不同,是static synchronized和synchronized最大的區別:

 1 public class SynchronizedTester {
 2 
 3     /*
 4      * testOne()和testTwo()均是對SynchronizedTester這個類的對象(實例)加上鎖
 5      * 也就是鎖對象的引用是SynchronizedTester這個類的對象(實例)
 6      * 兩種寫法基本等價
 7      */
 8     private synchronized void testOne(){
 9         //do something...
10     }
11     private void testTwo(){
12         synchronized(this){
13             //do something...
14         }
15     }
16 
17 }
 1 public class SynchronizedStaticTester {    
 2     /*
 3      * testOne()和testTwo()則是對SynchronizedTester這個類加上鎖
 4      * 也就是鎖對象的引用是SynchronizedTester這個類,不再是類的對象或類的實例
 5      * 兩種寫法基本等價
 6      */
 7     private static synchronized void testOne(){
 8         //do sth.
 9     }
10     
11     private static void testTwo(){
12         synchronized (SynchronizedStaticTester.class) { //Notice: Not this!
13             // do sth.
14         }
15     }
16     
17     
18 
19 }

synchronized的范圍是某個類的對象/實例,防止多個線程同時訪問同一個類對象/實例的synchronized代碼塊。

static synchronized地方范圍是某個類,防止多個線程同時訪問這個類的synchronized代碼塊。

知道區別以后看看題目:

1.出自並發編程網

 1 public class A {
 2 
 3     private static boolean isTrue;
 4     
 5     public static synchronized void staticWrite(boolean b){
 6           isTrue = b;
 7     }
 8 
 9     public static synchronized boolean staticRead(){
10           return isTrue;
11     }
12     
13     public synchronized void write(boolean b){
14           isTrue = b;
15     }
16 
17     public synchronized boolean read(){
18           return isTrue;
19     }
20 }

問題:

  1. 線程1訪問A.staticWrite(true)方法時,線程2能訪問A.staticRead()方法嗎?
  2. 線程1訪問new A().staticWrite(false)方法時,線程2能訪問new A().staticRead()方法嗎?
  3. 線程1訪問A.staticWrite(false)方法時,線程2能訪問new A().staticRead()方法嗎?

答案是都不能。因為無論A創建多少個對象或實例,任一對象調用staticWrite(),或類直接調用staticWrite(),鎖對象的引用都是A.class。也就是只要調用staticWrite方法,都會對A.class加鎖,而staticRead()需要獲得的鎖對象也正是A.class,因此會出現阻塞。所以線程2無法訪問A.staticRead()方法。

  1. A a=new A(); 線程1訪問a.write(false)方法,線程2能訪問a.read()方法嗎?
  2. A a=new A(); A b=new A();線程1訪問a.write(false)方法,線程2能訪問b.read()方法嗎?

題目1答案是不可以,理由與上面類似。a.write()對類A的對象a加了鎖,而a.read()需要獲得的鎖對象也剛好是a,所以線程2無法訪問a.read()

題目2答案是可以,a.write()對類A的對象加了鎖,而b.read()需要獲得的鎖對象則是b,兩者無沖突。b.read()能順利獲得鎖,並訪問read()方法。

再次總結:

  1. 對於實例同步方法,鎖是當前實例對象。
  2. 對於靜態同步方法,鎖是當前對象的Class對象。
  3. 對於同步方法塊,鎖是Synchonized括號里配置的對象。

 

再看一題,出自日本作者-結成浩的《java多線程設計模式》

1 pulbic class Something(){
2          public synchronized void isSyncA(){}
3          public synchronized void isSyncB(){}
4          public static synchronized void cSyncA(){}
5          public static synchronized void cSyncB(){}
6      }

有Something類的兩個實例x與y,那么下列組方法何以被1個以上線程同時訪問呢
   a.   x.isSyncA()與x.isSyncB() 
   b.   x.isSyncA()與y.isSyncA()
   c.   x.cSyncA()與y.cSyncB()
   d.   x.isSyncA()與Something.cSyncA()

有了上面的知識,很容易判斷a,c不能,而b可以。d稍微分析一下,也可以確定可以被1個以上線程同時訪問,因此x.isSyncA()鎖的是Something的對象x,而Something.cSyncA()鎖的是Something.class。

最后要留意的是:synchronized並不能繼承,子類覆蓋父類synchronized方法時,一定也要在前面加上synchronized關鍵字;但子類未重寫該方法,實例化子類,調用對應方法,也會加鎖

這篇文章對synchronized關鍵字做了深入的總結,值得仔細理解。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM