一、synchronized關鍵字:
- 基本使用與作用:通過搶票代碼示例,展示了
synchronized
作為對象鎖,可避免多線程超賣或搶到同一張票問題,保證代碼原子性,同一時刻只有一個線程獲得鎖,其他線程阻塞。 - 底層原理:底層是
monitor
,通過分析字節碼信息,可看到monitor enter
和monitor exit
,分別對應上鎖和解鎖,且有兩個monitor exit
以防異常導致鎖未釋放。 monitor
工作機制:monitor
有wait set
、entry list
、owner
三個屬性。線程嘗試獲取鎖時,若owner
為none
,則擁有鎖;否則進入entry list
等待,釋放鎖后喚醒entry list
中線程爭搶。調用wait
方法的線程進入wait set
。- 鎖升級:
monitor
是重量級鎖,因涉及用戶態和內核態切換,性能低。JDK1.6后引入偏向鎖和輕量級鎖。一個線程重復獲取鎖時用偏向鎖;兩個線程交替獲取鎖用輕量級鎖;多線程競爭時用重量級鎖。 - 三種鎖對比:偏向鎖性能最好,首次加鎖記錄線程ID并改標志,后續重入只需判斷;輕量級鎖每次加鎖記錄都需CAS操作;重量級鎖涉及用戶態和內核態切換,性能最低。
二、Java內存模型(JMM)面試題:
- 概念:定義了共享變量在多線程讀寫操作的行為規范,保證內存讀寫指令正確性。
- 內存劃分:分為工作內存和主內存。工作內存是線程私有,存儲線程內私有數據,線程間無法互相訪問;主內存是共享區域,存儲共享變量,多線程同步數據需通過主內存。
- CAS操作面試題:
- 概念與原理:全稱
compare and swap
,體現樂觀鎖思想,在無鎖情況下保證線程操作共享數據的原子性。線程操作共享數據時,先讀取主內存數據到工作內存,修改后與主內存數據比對,相同則更新,不同則自旋重試。 - 底層實現:依賴
unsafe
類調用操作系統底層CAS指令,unsafe
類提供compare and swap object
、int
、long
三個本地方法。 - 與鎖的對比:CAS是樂觀鎖,不怕其他線程修改共享變量,通過自旋重試;
synchronized
是悲觀鎖,上鎖后防止其他線程修改。
- 概念與原理:全稱
三、volatile`關鍵字:
-
含義:可修飾共享變量,保證線程間可見性和禁止指令重排序。
-
線程間可見性:通過代碼示例,展示了未加
volatile
時,線程修改共享變量,其他線程不可見,原因是JIT編譯器優化。可通過加VM參數禁用JIT或加volatile
關鍵字解決,項目開發中推薦使用volatile
關鍵字。 -
指令重排序與volatile關鍵字:
- 指令重排序概念:代碼執行順序可能與編寫順序不一致,通過測試代碼可能出現結果為10的情況判斷是否發生重排序,需使用高并發多線程測試工具。
- volatile解決重排序:在共享變量讀寫時加入不同屏障,阻止其他讀寫操作越過屏障。如加在y上能解決問題,加在x上不行。
- volatile使用技巧:寫變量時讓volatile修飾的變量在代碼最后位置,讀變量時讓其在代碼最上面。
- volatile特性總結:保證線程間可見性,禁止指令重排序。
四、AQS(抽象隊列同步器):
- AQS定義與對比:是JUC中提供的鎖機制,與synchronized對比,它是Java API,需手動開關鎖,在鎖競爭激烈時性能更好。
- 基本工作機制:內部有volatile修飾的state,0表示無鎖,1表示有鎖。線程請求鎖需修改state,失敗則進入隊列等待,隊列為先進先出的雙向隊列。
- 保證原子性:多個線程搶資源時用CAS設置保證原子性。
- 公平鎖與非公平鎖:非公平鎖是新線程與隊列中線程共同搶資源;公平鎖是新線程到隊列尾部等待,按隊列順序獲得鎖。
- AQS總結:是隊列同步器,維護雙向隊列,用state控制鎖,使用CAS保證原子性。
五、ReentrantLock(可重入鎖):
- 特點:可中斷、可設置超時時間、可設置公平鎖、支持多個條件變量,與synchronized一樣支持重入。
- 實現原理:底層使用CAS和AQS,構造函數可創建公平鎖或非公平鎖。
- 工作方式:與AQS類似,用CAS修改狀態搶鎖,搶到鎖將線程掛到屬性中,釋放鎖喚醒隊列中線程。
- 總結:是可重入鎖,底層用CAS和AQS,支持公平和非公平鎖。
六、synchronized與Lock對比:
- 語法層面:synchronized是關鍵字,由JVM提供,C++實現,退出同步塊自動釋放鎖;Lock由JDK提供,Java實現,需調用unlock釋放鎖。
- 功能層面:都屬悲觀鎖,有互斥同步和鎖重入功能,但Lock有公平鎖、可打斷、可超時、多條件變量等功能,通過代碼演示驗證。
- 性能層面:無競爭時synchronized有優化,性能還行;競爭激烈時Lock性能更好。
七、死鎖問題:
- 死鎖產生條件:一個線程同時獲取多把鎖易發生死鎖,如線程T1持有a鎖等b鎖,線程T2持有b鎖等a鎖。
- 死鎖診斷工具:使用jps輸出JVM中運行的所有進程狀態信息,使用jstack查看Java進程內的線程堆棧信息;還可使用jconsole和VisualVM等可視化工具。
八、ConcurrentHashMap:
- JDK1.7結構:采用分段的數組加列表實現,數組不可擴容,添加數據時用ReentrantLock加鎖,性能較低。
- JDK1.8結構:采用數組加鏈表加紅黑樹結構,添加新節點用CAS自旋鎖,有鏈表或紅黑樹時用synchronized鎖首節點,性能更好。
- 總結:不同版本底層數據結構和加鎖方式不同,1.8加鎖力度更細。
九、并發程序問題根源:
- 原子性:一個線程在CPU中的操作不可暫停或中斷,如搶票代碼可能出現超賣,可通過synchronized或Lock鎖保證。
- 可見性:一個線程對共享變量修改后讓另一個線程可見,可加volatile關鍵字解決,加鎖也可但性能不高。
- 有序性:處理器可能對代碼進行指令重排,加volatile關鍵字可禁止指令重排序。