天天看點

多線程 - Synchronized 篇

一、Synchronized 簡介

多線程 - Synchronized 篇
多線程 - Synchronized 篇
多線程 - Synchronized 篇
多線程 - Synchronized 篇

Ps1:join() 方法作用:等待線程執行完後才會執行下面的接下來的語句。

Ps2:兩個線程同時a++, 最後結果會比預計的少(讀取a, a++, 将a寫入記憶體),是以每一步執行完都有可能被打斷,是以a值有可能沒寫進記憶體就執行另一個線程的a++操作了,so...

二、Synchronized的兩種用法(對象鎖和類鎖)

多線程 - Synchronized 篇

1、對象鎖

多線程 - Synchronized 篇
多線程 - Synchronized 篇

Ps:這裡的 while 相當于 join 作用。

1.1、this

多線程 - Synchronized 篇

Ps:【兩個線程同時通路一個對象的同步方法】。

多線程 - Synchronized 篇

1.2、lock(手動指定)

多線程 - Synchronized 篇
多線程 - Synchronized 篇

Ps:這裡就展現出自定義鎖lock與this的應用差別。Lock1和lock2是并行運作的,而單個lockN屬于同步運作的。

多線程 - Synchronized 篇

Ps:如果兩個塊都為同一個lockN,那麼與this的應用一樣。

1.2、附:調試技巧(檢視線程生命周期)

多線程 - Synchronized 篇

Ps:調試技巧1:

All:所有線程在這裡都會停下來。

Thread:隻針對目前該線程會停下來,其餘線程繼續執行。

多線程 - Synchronized 篇

Ps:調試技巧2:

檢視此時此刻的屬性、狀态等。

步驟:

Debugger -> Frames -> 748,Thread -> Evaluate -> 輸入表達式。

多線程 - Synchronized 篇

Ps:調試技巧3:

切換線程檢視此時此刻的屬性、狀态等。

步驟:

Debugger -> Frames -> 748,Thread -> Evaluate -> 輸入表達式。

1.3、普通方法鎖

多線程 - Synchronized 篇

2、類鎖

多線程 - Synchronized 篇
多線程 - Synchronized 篇

2.1、synchronized加在static方法上

多線程 - Synchronized 篇

Ps:不加 static,因為執行個體對象不一樣,是以會導緻并發運作。

多線程 - Synchronized 篇

Ps1:加 static,因為static代表類對象,是以把整個類對象鎖住,是以隻要是該類的執行個體對象,都會被鎖住,實作同步運作【兩個線程通路的是synchronized的靜态方法】。

Ps2:有些人說可以加在run方法上嗎?不行的。因為這裡的run是接口的實作,是以通常做法是另外寫一個靜态方法,一舉兩得(達到類鎖的作用也可以被執行在run方法裡)。

2.2、synchronized(*.class)代碼塊

多線程 - Synchronized 篇

Ps:原因:因為執行個體對象不一樣,是以可以并發運作,而且資料之間不會互相幹擾。【兩個線程通路的是兩個對象的同步方法】。

多線程 - Synchronized 篇

三、多線程通路同步方法的7種具體情況

多線程 - Synchronized 篇
多線程 - Synchronized 篇
多線程 - Synchronized 篇

Ps:【同時通路同步方法與非同步方法】。

多線程 - Synchronized 篇

Ps:【通路同一個對象的不同的普通同步方法】。

多線程 - Synchronized 篇

Ps:不同對象針對這種情況,也是可以并發運作的,互不幹擾。【同時通路靜态synchronized和非靜态synchronized方法】【不要看輸出,沒說服力,自己能了解就好】。

多線程 - Synchronized 篇

Ps:方法抛異常後,會釋放鎖。展示不抛出異常前和抛出異常後的對比:一旦抛出了異常,第二個線程會立刻進入同步方法,意味着鎖已經釋放。【方法抛異常後,會釋放鎖】

多線程 - Synchronized 篇

Ps:附加一種情況:sychronized 修飾的方法中調用沒有sychronized修飾的方法 A,則 A 為不安全方法,可被多個方法調用哦!

四、Synchronized的性質

性質1:可重入

多線程 - Synchronized 篇
多線程 - Synchronized 篇

可重入:一個線程拿到了鎖,這個線程可以再次使用該鎖對其他方法,說明該鎖是可以重入的。

不可重入:一個線程拿到鎖了,如果需要再次使用該鎖,必須先釋放該鎖才能再次擷取。

多線程 - Synchronized 篇

Ps:情況1:證明同一個方法是可重入的。

多線程 - Synchronized 篇

Ps:情況2:證明可重入不要求是同一個方法。

多線程 - Synchronized 篇

Ps:情況3:證明可重入不要求是同一個類中的。

總結:可重入鎖,粒度是線程範圍的,隻要在一個線程中,如果需要的是同一把鎖,那麼就不需要重新釋放鎖,再擷取鎖。

性質2:不可中段

多線程 - Synchronized 篇

五、深入原理

多線程 - Synchronized 篇

1、加鎖和釋放鎖的原理

多線程 - Synchronized 篇
多線程 - Synchronized 篇

Ps:這裡說明了 synchronized 相當于是 lock 的一個封裝,是以 lock 會比 synchronized 更細粒度控制。

多線程 - Synchronized 篇

Ps:反編譯素材。

多線程 - Synchronized 篇

Ps:.class檔案進行反編譯指令:javap - verbose Demo.class

多線程 - Synchronized 篇

Ps1:反編譯結果看到有兩個關鍵的底層代碼:monitorenter & monitorexit來控制加鎖和釋放鎖。

Ps2:Monitorenter:線程第一次給對象加鎖的時候,計數變為1。每當這個相同的線程在此對象上再次獲得鎖時,計數會遞增

Ps3:Monitorexit:每當任務離開時,計數遞減。當計數為0的時候,鎖被完全釋放。

2、可重入原理:加鎖次數計數器

多線程 - Synchronized 篇

3、可見性原理:Java記憶體模型

多線程 - Synchronized 篇

Ps:三步驟:

(1)從記憶體中讀取(複制)變量;

(2)修改此變量(副本);

(3)把這個變量副本再寫入内容中去替換原先被複制的變量。

六、Synchronized的缺陷

多線程 - Synchronized 篇
多線程 - Synchronized 篇

Ps:Lock鎖可以克服synchronized的缺陷。

七、常見的面試問題

多線程 - Synchronized 篇
多線程 - Synchronized 篇

Ps:鎖對象為空案例,因為鎖是儲存在對象頭當中的,如果連對象都沒有,那何來對象頭。

多線程 - Synchronized 篇

Ps:死鎖案例:

當A線程通路

鎖1{

  鎖2{

  }

}

當B線程通路

鎖2{

  鎖1{

  }

}

那麼,很容易引發死鎖問題。

最後,JAVA專家建議:一般情況下建議util包中的并發類(至少比我們自己的完善),接着使用 synchronized(減少編碼,減少錯誤),如果要用到Lock獨有的特性,最後再考慮使用Lock。

八、思考題

多線程 - Synchronized 篇

九、總結

繼續閱讀