synchronized
和volatile
兩個關鍵字是Java併發程式設計中經常用到的兩個關鍵字。我們知道synchronized
可以保證併發程式設計中不會出現原子性、可見性和有序性問題,而volatile只能保證可見性和有序性,那麼,既然有了synchronized
,為什麼還需要volatile
?
Java中為了解決多執行緒中存在的原子性、可見性和有序性問題,提供了一系列和併發處理相關的關鍵字,例:synchronized、volatile、final、concurren包等。
synchronized
通過加鎖的方式,使得其在需要原子性、可見性和有序性這三種特性的時候都可以作為其中一種解決方案,看起來是“萬能”的。的確,大部分併發控制操作都能使用synchronized
來完成。volatile
通過在volatile
變數的操作前後插入在JVM虛擬機器記憶體屏障(JMM)的方式,保證了變數在併發場景下的可見性和有序性(作業系統的實現機制,彙編指令lock addl $0x0,(%rsp)
以及快取一致性協議(MESI))。volatile
關鍵字是無法保證原子性的,而synchronized
通過jvm層指令monitorenter
和monitorexit
兩個指令,可以保證被synchronized
修飾的程式碼在同一時間只能被一個執行緒訪問,即可保證不會出現CPU時間片在多個執行緒間切換,即可保證原子性。synchronized
實現的鎖本質上是一種阻塞鎖,也就是說多個執行緒要排隊訪問同一個共享物件。而volatile
是Java虛擬機器提供的一種輕量級同步機制,他是基於記憶體屏障實現的。說到底,他並不是鎖,所以他不會有synchronized
帶來的阻塞和效能損耗的問題。
- 在一般的程式中只要加上了synchronized就不需要再加上
volatile
,但是在單例模式則需要。這是因為為了防止指令重排。
注意:
- 本文中強調了JVM層面指令與作業系統層面的指令,請讀者不要混淆。
在作業系統中帶有lock字首的的指令在多核處理器會發生兩件事:
2.1 將當前處理器快取行的資料寫回到系統記憶體裡面去
2.2 這個寫回記憶體的操作會使其他CPU快取行的資料無效
本作品採用《CC 協議》,轉載必須註明作者和本文連結