一、Synchronized 簡介

Ps1:join() 方法作用:等待線程執行完後才會執行下面的接下來的語句。
Ps2:兩個線程同時a++, 最後結果會比預計的少(讀取a, a++, 将a寫入記憶體),是以每一步執行完都有可能被打斷,是以a值有可能沒寫進記憶體就執行另一個線程的a++操作了,so...
二、Synchronized的兩種用法(對象鎖和類鎖)
1、對象鎖
Ps:這裡的 while 相當于 join 作用。
1.1、this
Ps:【兩個線程同時通路一個對象的同步方法】。
1.2、lock(手動指定)
Ps:這裡就展現出自定義鎖lock與this的應用差別。Lock1和lock2是并行運作的,而單個lockN屬于同步運作的。
Ps:如果兩個塊都為同一個lockN,那麼與this的應用一樣。
1.2、附:調試技巧(檢視線程生命周期)
Ps:調試技巧1:
All:所有線程在這裡都會停下來。
Thread:隻針對目前該線程會停下來,其餘線程繼續執行。
Ps:調試技巧2:
檢視此時此刻的屬性、狀态等。
步驟:
Debugger -> Frames -> 748,Thread -> Evaluate -> 輸入表達式。
Ps:調試技巧3:
切換線程檢視此時此刻的屬性、狀态等。
步驟:
Debugger -> Frames -> 748,Thread -> Evaluate -> 輸入表達式。
1.3、普通方法鎖
2、類鎖
2.1、synchronized加在static方法上
Ps:不加 static,因為執行個體對象不一樣,是以會導緻并發運作。
Ps1:加 static,因為static代表類對象,是以把整個類對象鎖住,是以隻要是該類的執行個體對象,都會被鎖住,實作同步運作【兩個線程通路的是synchronized的靜态方法】。
Ps2:有些人說可以加在run方法上嗎?不行的。因為這裡的run是接口的實作,是以通常做法是另外寫一個靜态方法,一舉兩得(達到類鎖的作用也可以被執行在run方法裡)。
2.2、synchronized(*.class)代碼塊
Ps:原因:因為執行個體對象不一樣,是以可以并發運作,而且資料之間不會互相幹擾。【兩個線程通路的是兩個對象的同步方法】。
三、多線程通路同步方法的7種具體情況
Ps:【同時通路同步方法與非同步方法】。
Ps:【通路同一個對象的不同的普通同步方法】。
Ps:不同對象針對這種情況,也是可以并發運作的,互不幹擾。【同時通路靜态synchronized和非靜态synchronized方法】【不要看輸出,沒說服力,自己能了解就好】。
Ps:方法抛異常後,會釋放鎖。展示不抛出異常前和抛出異常後的對比:一旦抛出了異常,第二個線程會立刻進入同步方法,意味着鎖已經釋放。【方法抛異常後,會釋放鎖】
Ps:附加一種情況:sychronized 修飾的方法中調用沒有sychronized修飾的方法 A,則 A 為不安全方法,可被多個方法調用哦!
四、Synchronized的性質
性質1:可重入
可重入:一個線程拿到了鎖,這個線程可以再次使用該鎖對其他方法,說明該鎖是可以重入的。
不可重入:一個線程拿到鎖了,如果需要再次使用該鎖,必須先釋放該鎖才能再次擷取。
Ps:情況1:證明同一個方法是可重入的。
Ps:情況2:證明可重入不要求是同一個方法。
Ps:情況3:證明可重入不要求是同一個類中的。
總結:可重入鎖,粒度是線程範圍的,隻要在一個線程中,如果需要的是同一把鎖,那麼就不需要重新釋放鎖,再擷取鎖。
性質2:不可中段
五、深入原理
1、加鎖和釋放鎖的原理
Ps:這裡說明了 synchronized 相當于是 lock 的一個封裝,是以 lock 會比 synchronized 更細粒度控制。
Ps:反編譯素材。
Ps:.class檔案進行反編譯指令:javap - verbose Demo.class
Ps1:反編譯結果看到有兩個關鍵的底層代碼:monitorenter & monitorexit來控制加鎖和釋放鎖。
Ps2:Monitorenter:線程第一次給對象加鎖的時候,計數變為1。每當這個相同的線程在此對象上再次獲得鎖時,計數會遞增
Ps3:Monitorexit:每當任務離開時,計數遞減。當計數為0的時候,鎖被完全釋放。
2、可重入原理:加鎖次數計數器
3、可見性原理:Java記憶體模型
Ps:三步驟:
(1)從記憶體中讀取(複制)變量;
(2)修改此變量(副本);
(3)把這個變量副本再寫入内容中去替換原先被複制的變量。
六、Synchronized的缺陷
Ps:Lock鎖可以克服synchronized的缺陷。
七、常見的面試問題
Ps:鎖對象為空案例,因為鎖是儲存在對象頭當中的,如果連對象都沒有,那何來對象頭。
Ps:死鎖案例:
當A線程通路
鎖1{
鎖2{
}
}
當B線程通路
鎖2{
鎖1{
}
}
那麼,很容易引發死鎖問題。
最後,JAVA專家建議:一般情況下建議util包中的并發類(至少比我們自己的完善),接着使用 synchronized(減少編碼,減少錯誤),如果要用到Lock獨有的特性,最後再考慮使用Lock。