Java 复习 —— JMM基础
2019獨角獸企業重金招聘Python工程師標準>>>
基本內容
1、共享變量在線程間的可見性
2、synchronized實現可見性
3、volatile 實現可見性
1)指令重排序
2)as-if-serial
3)volatile 使用注意事項
4、volatile和synchronized的比較
1、可見性
一個線程對共享變量值的修改,能夠及時地被其他線程看到。
共享變量:如果一個變量在多個線程的工作內存中都存在副本,那么這個變量就是這幾個線程的共享變量。
Java內存模型(JMM):描述了Java程序中各種變量(共享變量)的訪問規則,以及在JVM中將變量存儲到內存和從內存中讀取出變量這樣的底層細節。
2、JMM 基本規則
1)所有的變量都存儲在在主內存中。
2)每個線程都有自己獨立的工作內存,里面保存該線程使用到的變量副本(主內存中該變量的一份拷貝)。
3)線程對共享變量的所有操作都必須在自己的工作內存中進行,不能直接從主內存中讀寫
4)不同線程之間無法直接訪問其他線程工作內存中的變量,線程間變量值的傳遞需要通過主內存來完成。
3、共享變量實現可見性的原理
1)首先在自己線程Thread1里修改共享變量x=1
2)然后更新主內存的x=1
3)其次其他線程Thread2從主內存中讀取x=1,更新自己線程的值,
這樣連續性的操作,可以保證任何一個線程的獨立內存中的共享變量都是最新的值!
4、Java層面實現可見性的方式
1)synchronized
2)volatile
3)concurrent 包
5、synchronized
1)線程解鎖前,必須把共享變量的最新值刷新到主內存中
2)線程加鎖時,將清空工作內存中共享變量的值,從而使用共享變量時需要從主內存中重新讀取最新的值。(注意:加鎖與解鎖需要同一把鎖)
6、synchronized 修飾時,JVM操作的步驟
1)首先獲得互斥鎖
2)清空工作內存
3)從主內存拷貝變量的最新副本到工作內存
4)執行代碼
5)將更改后的共享變量值刷新到主內存
6)釋放互斥鎖
7、重排序
代碼書寫的順序與實際執行的順序不同,指令重排序是編譯器或處理器為了提高程序性能而做的優化!
1)編譯器優化
2)指令優化
3)內存系統優化
最后的結果:有可能導致代碼的執行的順序與編寫順序不一致,但是可以提高CPU性能
8、as-if-serial
無論如何重排序,程序的運行結果都是保持一致的!
單線程中是不能會因為重排序帶來內存可見性的問題。
多線程則會由于重排序帶來共享變量不一致的問題。
9、導致共享變量在線程間不可見的原因
1)線程交叉執行。(原子性來保證)
2)重排序結合線程交叉執行。(原子性來保證)
3)共享變量未及時更新。(內存可見性來保證)
10、synchronized 修飾變量、修飾方法或代碼塊
1)擁有原子性
2)擁有內存可見性
3)重量級
所以他能夠實現線程間執行操作的安全性!
11、volatile 修飾變量
1)不保證原子性
2)擁有可見性
3)輕量級
12、關于 i++
1)首先讀取,從主內存中讀取i的值更新到當前工作內存中
2)其次改變,對i進行加1
3)最后更新,從當前工作內存中的值刷新到主內存中去
所以,這不是一個原子操作,在這個操作過程中勢必會導致線程間交互而導致值的混亂!解決方式就是保證 i++ 具有原子性
1)使用synchronized
2)使用Lock對象,concurrent 包中
Lock lock = new RentrantLock();
try{
? ? lock.lock();
? ? i++
}finally{
? ? lock.unlock();
}
13、volatile 使用場合
1)對變量的寫入操作不依賴其當前值,比如Boolean值,但是 i++ 或 i=i+5
2)該變量沒有包含在其他變量的不變式中,比如: low < high (這里我也不是很清楚)
注意:共享變量都必須是private
final 也實現了內存可見性,因為他的值是不可修改的!
14、結論
對一個共享變量不僅僅要關心他的寫,還關心他的讀,二者都要加鎖;
volatile是輕量級的,能使用,盡量使用!
轉載于:https://my.oschina.net/heweipo/blog/505891
總結
以上是生活随笔為你收集整理的Java 复习 —— JMM基础的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux运维实战练习-2015年9月1
- 下一篇: Tom's Classes