Java 可見性
內存模型
- 主存
- 所有線程都可以訪問
- 本地內存
- 每個線程私有的內存
- java 的所有變量都存儲在主內存中
- 每個線程有自己獨的工作內存,保存了該線程使用到的變量副本,是對主內存中變量的一份拷貝
- 每個線程不能訪問其他線程的工作內存,線程間變量傳遞需要通過主內存來完成
- 每個線程不能直接操作主存,只能把主存的內容拷貝到本地內存后再做操作(這是線程不安全的本質),然后寫回主存
可見性的方法
volatile
這種方式可以保證每次取數直接從主存取
它只能保證內存的可見性,無法保證原子性
它不需要加鎖,比 synchronized 更輕量級,不會阻塞線程
不會被編譯器優化
然而要求對這個變量做原子操作,否則還是會有問題
雖然 volatile 是輕量級,但是它也需要保證 讀寫的順序不亂序,所以可以有優化點,比如在單例實現方式中的雙重校驗中,使用 臨時變量
降低 volatile 變量的訪問。
synchronized
Synchronized 能夠實現原子性和可見性;在 Java 內存模型中,synchronized規 定,線程在加鎖時,先清空工作內存 → 在主內存中拷貝最新變量的副本到工作內存 → 執行完代碼 → 將更改后的共享變量的值刷新到主內存中 → 釋放互斥鎖。
所以如果無法用 volatile 做可見性,則可以考慮用 synchronized 可以做可見性的保證
AtomicXXX
jdk 提供了很多原子類型,這種類型的基本原理總結起來,volatile + unsafe 的 Compare and Swap,這種 Unsafe 操作並不推薦在自己的代碼中使用,因為各 JDK 版本在這里變化較大,有可能升級 JDK 時造成各種問題。而且也要保證自己能夠用好。
LongAdder
待補充