點關注,不迷路!如果本文對你有幫助的話不要忘記點贊支持哦!

上述面試題答案都整理成檔案筆記, 也還整理了一些面試資料&最新2020收集的一些大廠的面試真題(都整理成檔案,小部分截圖),有需要的可以 點擊進入暗號:csdn ,
眾所周知 Synchronize 關鍵字是解決并發問題常用解決方案,有以下三種使用方式:
- 同步普通方法,鎖的是當前物件,
- 同步靜態方法,鎖的是當前
Class物件, - 同步塊,鎖的是
{}中的物件,
實作原理:JVM 是通過進入、退出物件監視器( Monitor )來實作對方法、同步塊的同步的,
具體實作是在編譯之后在同步方法呼叫前加入一個 monitor.enter 指令,在退出方法和例外處插入 monitor.exit 的指令,
其本質就是對一個物件監視器( Monitor )進行獲取,而這個獲取程序具有排他性從而達到了同一時刻只能一個執行緒訪問的目的,
而對于沒有獲取到鎖的執行緒將會阻塞到方法入口處,直到獲取鎖的執行緒 monitor.exit 之后才能嘗試繼續獲取鎖,
流程圖如下:

image
通過一段代碼來演示:
public static void main(String[] args) {
synchronized (Synchronize.class){
System.out.println("Synchronize");
}
}
使用 javap -c Synchronize 可以查看編譯之后的具體資訊,
public class com.crossoverjie.synchronize.Synchronize {
public com.crossoverjie.synchronize.Synchronize();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // class com/crossoverjie/synchronize/Synchronize
2: dup
3: astore_1
**4: monitorenter**
5: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #4 // String Synchronize
10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: aload_1
**14: monitorexit**
15: goto 23
18: astore_2
19: aload_1
20: monitorexit
21: aload_2
22: athrow
23: return
Exception table:
from to target type
5 15 18 any
18 21 18 any
}
可以看到在同步塊的入口和出口分別有 monitorenter,monitorexit
指令,
鎖優化
synchronize 很多都稱之為重量鎖,JDK1.6 中對 synchronize 進行了各種優化,為了能減少獲取和釋放鎖帶來的消耗引入了偏向鎖和輕量鎖,
輕量鎖
當代碼進入同步塊時,如果同步物件為無鎖狀態時,當前執行緒會在堆疊幀中創建一個鎖記錄(Lock Record)區域,同時將鎖物件的物件頭中 Mark Word 拷貝到鎖記錄中,再嘗試使用 CAS 將 Mark Word 更新為指向鎖記錄的指標,
如果更新成功,當前執行緒就獲得了鎖,
如果更新失敗 JVM 會先檢查鎖物件的 Mark Word 是否指向當前執行緒的鎖記錄,
如果是則說明當前執行緒擁有鎖物件的鎖,可以直接進入同步塊,
不是則說明有其他執行緒搶占了鎖,如果存在多個執行緒同時競爭一把鎖,輕量鎖就會膨脹為重量鎖,
解鎖
輕量鎖的解鎖程序也是利用 CAS 來實作的,會嘗試鎖記錄替換回鎖物件的 Mark Word ,如果替換成功則說明整個同步操作完成,失敗則說明有其他執行緒嘗試獲取鎖,這時就會喚醒被掛起的執行緒(此時已經膨脹為重量鎖)
輕量鎖能提升性能的原因是�:認為大多數鎖在整個同步周期都不存在競爭,所以使用 CAS 比使用互斥開銷更少,但如果鎖競爭激烈,輕量鎖就不但有互斥的開銷,還有 CAS 的開銷,甚至比重量鎖更慢,
偏向鎖
為了進一步的降低獲取鎖的代價,JDK1.6 之后還引入了偏向鎖,
偏向鎖的特征是:鎖不存在多執行緒競爭,并且應由一個執行緒多次獲得鎖,
當執行緒訪問同步塊時,會使用 CAS 將執行緒 ID 更新到鎖物件的 Mark Word 中,如果更新成功則獲得偏向鎖,并且之后每次進入這個物件鎖相關的同步塊時都不需要再次獲取鎖了,
釋放鎖
當有另外一個執行緒獲取這個鎖時,持有偏向鎖的執行緒就會釋放鎖,釋放時會等待全域安全點(這一時刻沒有位元組碼運行),接著會暫停擁有偏向鎖的執行緒,根據鎖物件目前是否被鎖來判定將物件頭中的 Mark Word 設定為無鎖或者是輕量鎖狀態,
輕量鎖可以提高帶有同步卻沒有競爭的程式性能,但如果程式中大多數鎖都存在競爭時,那偏向鎖就起不到太大作用,可以使用 -XX:-userBiasedLocking=false 來關閉偏向鎖,并默認進入輕量鎖,
其他優化
適應性自旋
在使用 CAS 時,如果操作失敗,CAS 會自旋再次嘗試,由于自旋是需要消耗 CPU 資源的,所以如果長期自旋就白白浪費了 CPU,JDK1.6加入了適應性自旋:
如果某個鎖自旋很少成功獲得,那么下一次就會減少自旋,
到此這篇關于文章就結束了!
點關注,不迷路!如果本文對你有幫助的話不要忘記點贊支持哦!

上述面試題答案都整理成檔案筆記, 也還整理了一些面試資料&最新2020收集的一些大廠的面試真題(都整理成檔案,小部分截圖),有需要的可以 點擊進入暗號:csdn ,
希望對大家有所幫助,有用的話點贊給我支持!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/22492.html
標籤:其他
上一篇:使用 Lombok 釋放百行代碼
