既然synchronized是"萬能"的,為什麼還需要volatile呢?

Barnett_fly發表於2020-10-06

synchronizedvolatile兩個關鍵字是Java併發程式設計中經常用到的兩個關鍵字。我們知道synchronized可以保證併發程式設計中不會出現原子性、可見性和有序性問題,而volatile只能保證可見性和有序性,那麼,既然有了synchronized,為什麼還需要volatile

  1. Java中為了解決多執行緒中存在的原子性、可見性和有序性問題,提供了一系列和併發處理相關的關鍵字,例:synchronized、volatile、final、concurren包等。

  2. synchronized通過加鎖的方式,使得其在需要原子性、可見性和有序性這三種特性的時候都可以作為其中一種解決方案,看起來是“萬能”的。的確,大部分併發控制操作都能使用synchronized來完成。

  3. volatile通過在volatile變數的操作前後插入在JVM虛擬機器記憶體屏障(JMM)的方式,保證了變數在併發場景下的可見性和有序性(作業系統的實現機制,彙編指令lock addl $0x0,(%rsp)以及快取一致性協議(MESI))。

  4. volatile關鍵字是無法保證原子性的,而synchronized通過jvm層指令monitorentermonitorexit兩個指令,可以保證被synchronized修飾的程式碼在同一時間只能被一個執行緒訪問,即可保證不會出現CPU時間片在多個執行緒間切換,即可保證原子性。

  5. synchronized實現的鎖本質上是一種阻塞鎖,也就是說多個執行緒要排隊訪問同一個共享物件。而volatile是Java虛擬機器提供的一種輕量級同步機制,他是基於記憶體屏障實現的。說到底,他並不是鎖,所以他不會有synchronized帶來的阻塞和效能損耗的問題。

  1. 在一般的程式中只要加上了synchronized就不需要再加上volatile,但是在單例模式則需要。這是因為為了防止指令重排

注意:

  1. 本文中強調了JVM層面指令與作業系統層面的指令,請讀者不要混淆。
  2. 在作業系統中帶有lock字首的的指令在多核處理器會發生兩件事:
    2.1 將當前處理器快取行的資料寫回到系統記憶體裡面去
    2.2 這個寫回記憶體的操作會使其他CPU快取行的資料無效

本作品採用《CC 協議》,轉載必須註明作者和本文連結
jzhao

相關文章