爲何要這麼說呢, 由於筆者被這個坑過(實際上是本身坑本身)╮(╯_╰)╭java
先看一段synchronized 的詳解:併發
synchronized 是 java語言的關鍵字,當它用來修飾一個方法或者一個代碼塊的時候,可以保證在同一時刻最多隻有一個線程執行該段代碼。異步
1、當兩個併發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程獲得執行。另外一個線程必須等待當前線程執行完這個代碼塊之後才能執行該代碼塊。ide
2、然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另外一個線程仍然能夠訪問該object中的非synchronized(this)同步代碼塊。this
3、尤爲關鍵的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其餘線程對object中全部其它synchronized(this)同步代碼塊的訪問將被阻塞。spa
4、第三個例子一樣適用其它同步代碼塊。也就是說,當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就得到了這個object的對象鎖。結果,其它線程對該object對象全部同步代碼部分的訪問都被暫時阻塞。線程
5、以上規則對其它對象鎖一樣適用.code
簡單來講, synchronized就是爲當前的線程聲明一個鎖, 擁有這個鎖的線程能夠執行區塊裏面的指令, 其餘的線程只能等待獲取鎖, 而後才能相同的操做.對象
這個很好用, 可是筆者遇到另外一種比較奇葩的狀況.ci
1. 在同一類中, 有兩個方法是用了synchronized關鍵字聲明
2. 在執行完其中一個方法的時候, 須要等待另外一個方法(異步線程回調)也執行完, 因此用了一個countDownLatch來作等待
3. 代碼解構以下:
synchronized void a(){ countDownLatch = new CountDownLatch(1); // do someing countDownLatch.await(); } synchronized void b(){ countDownLatch.countDown(); }
其中
a方法由主線程執行, b方法由異步線程執行後回調
執行結果是:
主線程執行 a方法後開始卡住, 再也不往下作, 任你等多久都沒用.
這是一個很經典的死鎖問題
a等待b執行, 其實不要看b是回調的, b也在等待a執行. 爲何呢? synchronized 起了做用.
通常來講, 咱們要synchronized一段代碼塊的時候, 咱們須要使用一個共享變量來鎖住, 好比:
byte[] mutex = new byte[0]; void a1(){ synchronized(mutex){ //dosomething } } void b1(){ synchronized(mutex){ // dosomething } }
若是把a方法和b方法的內容分別遷移到 a1和b1 方法的synchronized塊裏面, 就很好理解了.
a1執行完後會間接等待(countDownLatch)b1方法執行
然而因爲 a1 中的mutex並無釋放, 就開始等待b1了, 這時候, 即便是異步的回調b1方法, 因爲須要等待mutex釋放鎖, 因此b方法並不會執行
因而就引發了死鎖
而這裏的synchronized關鍵字放在方法前面, 起的做用就是同樣的. 只是java語言幫你隱去了mutex的聲明和使用而已. 同一個對象中的synchronized 方法用到的mutex是相同的, 因此即便是異步回調, 也會引發死鎖, 因此要注意這個問題. 這種級別的錯誤是屬於synchronized關鍵字使用不當. 不要亂用, 並且要用對.
那麼這樣的 隱形的mutex 對象到底是 什麼呢?
很容易想到的就是 實例自己. 由於這樣就不用去定義新的對象了作鎖了. 爲了證實這個設想, 能夠寫一段程序來證實.
思路很簡單, 定義一個類, 有兩個方法, 一個方法聲明爲 synchronized, 一個在 方法體裏面使用synchronized(this), 而後啓動兩個線程, 來分別調用這兩個方法, 若是兩個方法之間發生鎖競爭(等待)的話, 就能夠說明 方法聲明的 synchronized 中的隱形的mutex其實就是 實例自己了.
public class MultiThreadSync { public synchronized void m1() throws InterruptedException{ System. out.println("m1 call" ); Thread. sleep(2000); System. out.println("m1 call done" ); } public void m2() throws InterruptedException{ synchronized (this ) { System. out.println("m2 call" ); Thread. sleep(2000); System. out.println("m2 call done" ); } } public static void main(String[] args) { final MultiThreadSync thisObj = new MultiThreadSync(); Thread t1 = new Thread(){ @Override public void run() { try { thisObj.m1(); } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread t2 = new Thread(){ @Override public void run() { try { thisObj.m2(); } catch (InterruptedException e) { e.printStackTrace(); } } }; t1.start(); t2.start(); } }
結果輸出是:
m1 call
m1 call done
m2 call
m2 call done
說明方法m2的sync塊等待了m1的執行. 這樣就能夠證明 上面的設想了.
另外須要說明的是, 當sync加在 static的方法上的時候, 因爲是類級別的方法, 因此鎖住的對象是當前類的class實例. 一樣也能夠寫程序進行證實.這裏略.
因此方法的synchronized 關鍵字, 在閱讀的時候能夠自動替換爲synchronized(this){}就很好理解了.
void method(){ void synchronized method(){ synchronized(this){ // biz code // biz code } ------>>> } }