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訪問A.staticWrite(true)方法時,線程2能訪問A.staticRead()方法嗎?
- 線程1訪問new A().staticWrite(false)方法時,線程2能訪問new A().staticRead()方法嗎?
- 線程1訪問A.staticWrite(false)方法時,線程2能訪問new A().staticRead()方法嗎?
答案是都不能。因為無論A創建多少個對象或實例,任一對象調用staticWrite(),或類直接調用staticWrite(),鎖對象的引用都是A.class。也就是只要調用staticWrite方法,都會對A.class加鎖,而staticRead()需要獲得的鎖對象也正是A.class,因此會出現阻塞。所以線程2無法訪問A.staticRead()方法。
- A a=new A(); 線程1訪問a.write(false)方法,線程2能訪問a.read()方法嗎?
- 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()方法。
再次總結:
- 對於實例同步方法,鎖是當前實例對象。
- 對於靜態同步方法,鎖是當前對象的Class對象。
- 對於同步方法塊,鎖是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關鍵字做了深入的總結,值得仔細理解。