并發(fā)編程-06線程安全性之可見性 (synchronized + volatile)
文章目錄
線程安全性文章索引
腦圖
可見性定義
導(dǎo)致不可見的原因
可見性 -synchronized (既保證原子性又保證可見性)
可見性 - volatile(但不保證操作的原子性)
volatile變量 寫操作
volatile變量 讀操作
使用volatile嘗試解決計數(shù)并發(fā)錯誤的問題 【volatile無法解決該問題】
volatile使用場景
synchronized和volatile的比較
代碼
線程安全性文章索引
并發(fā)編程-03線程安全性之原子性(Atomic包)及原理分析
并發(fā)編程-04線程安全性之原子性Atomic包的4種類型詳解
并發(fā)編程-05線程安全性之原子性【鎖之synchronized】
并發(fā)編程-06線程安全性之可見性 (synchronized + volatile)
并發(fā)編程-07線程安全性之有序性
腦圖
可見性定義
一個線程對共享變量值的修改,能夠及時的被其他線程看到。
導(dǎo)致不可見的原因
線程交叉執(zhí)行
重排序結(jié)合線程交叉執(zhí)行
共享變量更新后的值沒有在工作內(nèi)存與主內(nèi)存之間及時更新
結(jié)合我們前面說過的Java內(nèi)存模型,上述三個原因我們就很容易理解了。 不清楚的童鞋可以再回顧下 并發(fā)編程-02并發(fā)基礎(chǔ)CPU多級緩存和Java內(nèi)存模型JMM
可見性 -synchronized (既保證原子性又保證可見性)
synchronized能夠?qū)崿F(xiàn)原子性和可見性 。
JMM中關(guān)于Synchronized的規(guī)定
線程解鎖前,必須把共享變量的最新值刷新到主內(nèi)存
線程加鎖時,必須將工作內(nèi)存中的共享變量的值清空,從而使用共享變量時需要從主內(nèi)存中重新讀取最新的值。
【注意,加鎖和解鎖必須是同一把鎖】
可見性 - volatile(但不保證操作的原子性)
volatile可以保證 可見性和 有序性
通過加入內(nèi)存屏障和禁止重排序優(yōu)化來實現(xiàn)。
對volatile變量寫操作時,會在寫操作后加入一條store屏障指令,將本地內(nèi)存中的共享變量值刷新到主內(nèi)存
對volatile變量讀操作,會在讀操作前加入一條load指令屏障,從主內(nèi)存中讀取共享變量
volatile本質(zhì)是在告訴JVM當(dāng)前變量在寄存器中的值是不確定的,使用前,需要先從主存中讀取,因此可以實現(xiàn)可見性。而對n=n+1,n++等操作時,volatile關(guān)鍵字將失效,不能起到像synchronized一樣的線程同步(原子性)的效果。
volatile變量 寫操作
volatile變量 讀操作
使用volatile嘗試解決計數(shù)并發(fā)錯誤的問題 【volatile無法解決該問題】
即使將count用volatile修飾,每次從主存中取到的都是最新的值,可是當(dāng)多個線程同時取到最新的值,執(zhí)行+1操作,當(dāng)刷新到主存中的時候會覆蓋結(jié)果,從而丟失一些+1操作
volatile使用場景
多線程中使用volatile變量,對變量的寫入操作不能依賴當(dāng)前變量的值:如count++ .【 解釋下: count++不是原子操作,因為其可以分為:從主內(nèi)存中讀取count的值,在自己線程的工作內(nèi)存中將count的值+1,寫入最新的count的值到主內(nèi)存。 對于count++,線程A和線程B都執(zhí)行一次,最后輸出的count的值可能是1也可能是2】 。 比較適合 狀態(tài)標(biāo)記 場景
狀態(tài)標(biāo)記偽代碼
volatile boolean inited = false; // 線程A context = loadContext(); inited = true; // 線程B while(!inited ){ sleep(); } doSomethingWithConfig(context);
1
2
3
4
5
6
7
8
9
10
11
將inited 標(biāo)記為 volatile , 當(dāng)線程A更新inited后,因為是volatile,所以線程B可以及時從主內(nèi)存中感知到inited的改變。 這樣就確保了線程B中使用的contex是初始化過的。
double check (比較常見的比如單例模式中的double check)
synchronized和volatile的比較
synchronized保證內(nèi)存可見性和操作的原子性
volatile只能保證內(nèi)存可見性
volatile不需要加鎖,比synchronized更輕量級,并不會阻塞線程
volatile標(biāo)記的變量不會被編譯器優(yōu)化,而synchronized標(biāo)記的變量可以被編譯器優(yōu)化(如編譯器重排序的優(yōu)化).
volatile是變量修飾符,僅能用于變量,而synchronized是一個方法或塊的修飾符。
代碼
https://github.com/yangshangwei/ConcurrencyMaster
Java 任務(wù)調(diào)度
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。