目錄
一、核心作用
二、實現原理:基于"對象鎖"
三、使用方式
四、鎖的優化
五、優缺點
六、總結
synchronized
是 Java 中用于解決多線程并發安全問題的核心關鍵字,它的主要作用是實現線程間的同步,確保多個線程在訪問共享資源時的安全性。
一、核心作用
- 保證原子性
被synchronized
修飾的代碼塊或方法,在同一時間只能被一個線程執行,確保代碼塊內的操作不會被其他線程中斷,從而保證操作的原子性(不可分割)。 - 保證可見性
線程釋放鎖時,會將共享變量的最新值刷新到主內存;線程獲取鎖時,會從主內存重新讀取共享變量的值,避免線程使用"過期"數據。 - 保證有序性
通過限制線程執行順序,間接避免了指令重排序導致的并發問題(類似"天然的內存屏障"效果)。
二、實現原理:基于"對象鎖"
synchronized
的核心是"鎖",Java 中任何對象都可以作為鎖(內置鎖/監視器鎖),其實現依賴于 JVM 中的 對象頭(Mark Word) 和 監視器(Monitor):
- 對象頭:存儲對象的鎖狀態(無鎖、偏向鎖、輕量級鎖、重量級鎖),JVM 通過更新對象頭的標志位來實現鎖的獲取和釋放。
- 監視器(Monitor):一種同步工具,包含等待隊列(存放阻塞的線程)和持有線程等信息,確保同一時間只有一個線程能持有鎖。
當線程進入 synchronized
代碼時,會嘗試獲取鎖(修改對象頭的鎖狀態);執行完畢后釋放鎖,其他線程才能競爭鎖。
三、使用方式
1.修飾實例方法
鎖對象是當前實例(this
),多個線程訪問同一個實例的該方法時會同步,訪問不同實例則不會。
public synchronized void doSomething() {// 同步代碼
}
2.修飾靜態方法
鎖對象是當前類的 Class
對象(全局唯一),所有線程訪問該靜態方法時都會同步,無論實例是否相同。
public static synchronized void staticMethod() {// 同步代碼
}
3.修飾代碼塊
手動指定鎖對象(可自定義對象),靈活性更高,能縮小同步范圍以減少性能開銷。
public void method() {// 非同步代碼synchronized (lockObject) { // lockObject 可以是任意對象// 同步代碼塊}// 非同步代碼
}
四、鎖的優化
java 6
之后 JVM 對 synchronized
進行了多次優化,使其性能大幅提升:
- 偏向鎖
適用于單線程反復獲取鎖的場景。當線程第一次獲取鎖時,標記對象頭為"偏向模式",記錄線程 ID,后續該線程可直接進入同步代碼,無需競爭。 - 輕量級鎖
適用于多線程交替執行同步代碼的場景。線程獲取鎖時,通過 CAS 操作在棧幀中創建鎖記錄并修改對象頭,避免重量級鎖的內核態操作。 - 重量級鎖
適用于多線程同時競爭鎖的場景。此時會關聯監視器(Monitor),未獲取鎖的線程會進入阻塞狀態(CPU 開銷較大)。 - 自適應自旋
當線程獲取輕量級鎖失敗時,不會立即阻塞,而是自旋重試(空循環),JVM 會根據歷史重試成功率動態調整自旋次數。
五、優缺點
- 優點:
簡單易用,無需手動釋放鎖;JVM 自動管理鎖的升級和釋放,安全性高;在低并發場景下性能優異。 - 缺點:
重量級鎖會導致線程阻塞/喚醒(內核態操作),高并發下性能可能下降;鎖的釋放由 JVM 自動完成,無法手動干預(如設置超時)。
六、總結
synchronized
是 Java 并發編程的基礎,通過"對象鎖"機制保證了原子性、可見性和有序性。其底層實現經歷了從重量級鎖到偏向鎖、輕量級鎖的優化,理解 synchronized
的工作原理。
下一篇會詳細介紹synchronized
?的實現原理,這里簡單的了解一下synchronized
?的理解以及作用