亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

Java并發編程系列(一)----深入剖析volatile關鍵字

2019-11-14 12:29:57
字體:
來源:轉載
供稿:網友

從內存模型講起

這里寫圖片描述

由于CPU的高速運算,主內存的頻率遠遠達不到要求,因此需要將用到的數據進行緩存,比如一個很簡單的操作:

i=i+1;

這樣CPU首先會從主內存中將i的值讀到CPU的高速緩存(工作內存)中,然后進行加1操作,完事之后將新的值寫入到主內存中。有人會想,MDZZ,這有什么好說的,看似簡單的一小步,確是并發編程爬坑的一大步。

假如i的值為0,兩個線程進行操作,最終的結果一定是2嗎?想象一下這樣的一種情況:

Thread1在CPU1上讀取i的值,此時Thread2在CPU2上也讀取了i的值。 Thread1加1操作后寫回主內存,而此時Thread2中緩存的還是0,加1后又寫回主內存,此時i的值是1而不是2。

這就是多線程下的緩存一致性問題,那么在多線程環境下是如何解決的呢,有兩種方法:

總線加鎖。 通過緩存一致性協議。

總線加鎖是一種獨占的方式占用內存,導致加鎖的內存同一時間只能被一個cpu訪問,比如,Thread1對i=i+1使用了總線鎖,從開始執行到結束的過程,其他線程都無法訪問該內存,也就不存在一致性問題。

緩存一致性協議的思想是:當CPU往主內存寫數據時,如果發現操作的變量是共享變量,即在其他CPU中也存在該變量的副本,會發出信號通知其他CPU將該變量的緩存行置為無效狀態,因此當其他CPU需要讀取這個變量時,發現自己緩存中緩存該變量的緩存行是無效的,那么它就會從內存重新讀取。

并發編程的三個性質

1. 原子性

所謂的原子性,指的是要么執行成功,要么不執行。這看起來好像又是很簡單,舉個例子:

i++;

這個是原子操作嗎?很多人想當然地認為是,事實上卻是三個操作:1.讀取i的值。2.將i的值加1。3.寫入新的值。如果在多線程的環境下,并發的結果和期望值會不一致,比如有這么一個Counter類,就是一個簡單的自增操作:

package com.rancho945.concurrent;public class Counter { public int count = 0; public void increase() { count++; }}

然后開個20線程,每個線程對同一個對象進行一萬次自增操作:

package com.rancho945.concurrent;public class Test { PRivate static final int THREAD_COUNT = 20; public static void main(String[] args) { final Counter counter = new Counter(); for (int i = 0; i < THREAD_COUNT; i++) { new Thread(new Runnable() { @Override public void run() { for (int j = 0; j < 50000; j++) { counter.increase(); } } }).start(); } // 等待所有線程執行完畢 while (Thread.activeCount() > 1) { Thread.yield(); } System.out.println(counter.count); }}

每次運行的結果都會有很大的不一樣。就是因為每個線程都緩存了副本,造成數據的不一致,原理跟前面分析的i=i+i是一樣的。我們看一下Counter類的字節碼,在increase方法中,一個自增操作從getfield到putfield包含了四條字節碼,并不是原子操作(這里只是假設一每條字節碼執行都是原子操作也是不嚴謹的,每一條字節碼指令都有可能分幾步執行)

public class com.rancho945.concurrent.Counter { public int count; public com.rancho945.concurrent.Counter(); Code: 0: aload_0 1: invokespecial #10 // Method java/lang/Object."<init>":()V 4: aload_0 5: iconst_0 6: putfield #12 // Field count:I 9: return public void increase(); Code: 0: aload_0 1: dup 2: getfield #12 // Field count:I 5: iconst_1 6: iadd 7: putfield #12 // Field count:I 10: return}

2. 可見性

看一段代碼

package com.rancho945.concurrent;public class WorkingTask implements Runnable{ private boolean stop = false; @Override public void run() { while(!stop){ //some task } } public void stopTask(){ stop = true; }}

這個相信是很多人見過的一段代碼,假如線程1正在執行任務,線程2調用了stopTask,線程1能被終止嗎?絕大多數情況下是可以的,但在極少數情況下停止不了,就會出現迷之bug,原因是當stop變為true時,還沒有來得及把值寫回主內存中,就去做其他事或者阻塞了,導致線程1進入死循環。這就是所謂的可見性問題。事實上前面原子性的例子里面也有可見性的問題,就是當一個線程完成值的修改,重新寫入主內存中后,其他線程緩存了舊值,無法感知到新值的改變。

3.有序性

再看一段代碼

package com.rancho945.concurrent;public class Gift { //表示是否完成禮物的制作 private boolean isInit = false; private String gift = ""; public void makeGift(){ System.out.println("正在制作禮物"); gift = "heart"; System.out.println("禮物制作完成"); isInit = true; } public void sendGift(){ while(!isInit){ System.out.println("禮物還沒準備好,再等等"); } System.out.println("女神,我有東西想送給你"+gift); }}

這講的是一個凄美的愛情故事,一個禮物店的老板(線程1)制作禮物(makeGift),當禮物制作好就在禮物盒上打個制作完成的標記(isInit=true);某個屌絲碼農(線程2)送禮物給女神(sendGift),當禮物未完成制作時等待,完成后(isInit=true)就送出去。這看起來也沒有什么問題,但事實上有一定幾率送出的禮物是空的,此時就會被女神呵呵了。

讓我們來看看這個程序為什么會出現這樣的問題,看makeGift的代碼:

照我們的理解,程序是從上到下往下執行的,但是編譯器或者虛擬機在執行的時候有可能會對其進行重排序,也就是先執行isInit=true后再去初始化禮物,如果線程1剛執行完isInit= true但還沒有完成禮物初始化時候線程2剛好讀取到isInit的值,就把禮物送出去了,此時女神將會收到一個空禮物盒。結果就呵呵了。

那么有人就會問,如果是這樣到處重排序,世界都亂了,重排序有一個原則,就是在單線程環境下能夠保證上下文語義的正確性,看下面的一段代碼:

i=10;k=i+8;m=10;

k的值依賴于i的值,因此在執行k=i+8之前必須保證i=10執行完畢。然而m=10可以在i=10之前執行,或者是在k=i+8之前執行,這都不會對其結果產生影響,因此JVM虛擬機可能會對執行順序進行重排序從而獲取更好的性能。那么我們在重新看前面禮物的例子就可以解釋為什么會出現禮物沒有制作好就會送出去了。(說得好像送禮物給女神就會逆襲一樣)

volatile語義

瞎逼逼了半天,終于開始講volatile了,那么volatile有什么用,它有兩層語義:

保證更改的內容會馬上寫回到主內存中,并且強制該變量在其他線程中的緩存無效,使改變后的變量對其他線程立即可見。 保證在對該變量操作之前的指令執行完畢,也就是禁止重排序。

可見性

如果我們將前面可見性例子中的stop變量使用volatile修飾,那么就可以保證改變的值對其他線程立即可見,從而避免停止任務失敗的情況。

有序性

如果用volatile修飾Gift類中的isInit變量,就不會出現禮物為空的情況,因為volatile可以保證在執行volatile變量操作之前所有的操作都已經執行完畢,也就是isInit=true的執行不會被重排序到前面(注:volatile重排序語意在JDK1.5之后才完全修復,意味著在1.5之前仍然不能保證不進行重排序)。

原子性

那么volatile能夠保證原子性嗎? 很多人覺得既然volatile變量的改變對所有的寫操作對其他的線程立即可見,也就是該變量在各個線程中是一致的,那么也就是線程安全的。這個論據是沒有錯,但是結論是錯的。我們把前面原子性的示例代碼稍微做一下改動:

package com.rancho945.concurrent;public class Counter { //注意這里改成了volatile public volatile int count = 0; public void increase() { count++; }}

看一下它的字節碼:

public void increase(); Code: 0: aload_0 1: dup 2: getfield #12 // Field count:I 5: iconst_1 6: iadd 7: putfield #12 // Field count:I 10: return

volatile能夠保證的是getstatic指令加載變量時變量值是最新的,但是在執行iconst_1和iadd指令的時候,變量的值有可能被其他線程改變了,而此時變量的值仍然不是正確的,因此volatile不能保證原子性。

volatile關鍵字的應用

volatile關鍵字的應用一般來說遵循兩個原則:

變量值不依賴當前狀態。 變量不出現在其他表達式中。

第一個原則提現就是前面的i++,因為i++操作依賴當前i的值,所以不能用volatile保證其線程安全。 第二個原則的體現為:

volatile int i;//該表達式依賴了i,不能保證其線程安全k = i+10;

volatile常見的兩個應用場景:

標記位

public class WorkingTask implements Runnable{ private volatile boolean stop = false; @Override public void run() { while(!stop){ //some task } } public void stopTask(){ stop = true; }}

這個前面已經說過了,不在贅述。

單例模式的雙重檢查

class Singleton{ private static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if(instance==null) { //1 synchronized (Singleton.class) { //2 if(instance==null) //3 instance = new Singleton(); //4 } } return instance; }}

這里的單例有什么問題呢?我們來看一下,假設有這么一個過程:

線程A和線程B都執行到1處。 線程A執行到2,獲取到類鎖,此時線程B阻塞。 線程A執行到3,發現實例為空,執行到4處。

關鍵點到了,正常情況下,4處的執行順序為:

為對象申請內存。 實例化對象。 將對象的內存引用賦給instance。

由于可能進行了重排序,4處的執行順序為:

為對象申請內存。 將對象的內存引用賦給instance。 實例化對象。

如果進行了重排序,那么在將對象內存賦給instance后(此時對象尚未初始化完成),線程A退出了同步塊,線程B進入了3,發現instance不為null,直接將沒有示例化完成的對象返回,導致獲取的實例結果并不正確。 因此,在單例的引用使用volatile變量修飾,可以保證執行4的時候不被重排序,從而保證單例構造的正確性。

class Singleton{ //注意這里使用了volatile修飾 private volatile static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if(instance==null) { //1 synchronized (Singleton.class) { //2 if(instance==null) //3 instance = new Singleton(); //4 } } return instance; }}

題外話

懶漢模式的另外一種優雅的實現方式

public class Singleton { private Singleton() {} public static class Holder { static Singleton instance = new Singleton(); } public static Singleton getInstance() { return Holder.instance; } }

該方法通過內部類的靜態變量來實現,由內部類的加載和初始化來保證線程安全。

參考資料

《深入理解Java虛擬機》 周志明 著 《Java并發編程的藝術》 方騰飛 著 《Java并發編程實戰》 Brian Goetz等著 童云蘭等譯 單例模式與雙重檢測 http://www.49028c.com/topic/652440 Java內存模型 http://ifeve.com/java-memory-model-6/
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国内成人精品一区| 日韩国产欧美区| 亚洲欧美中文字幕| 久久69精品久久久久久久电影好| 国产91精品黑色丝袜高跟鞋| 日韩国产一区三区| 亚洲精品日韩丝袜精品| 精品呦交小u女在线| 精品亚洲永久免费精品| 成人福利视频在线观看| 色悠悠国产精品| 中文字幕亚洲天堂| 中文字幕成人在线| 日韩免费在线视频| 九九热在线精品视频| 亚洲人成免费电影| 国产成人精品一区二区在线| 日韩精品免费综合视频在线播放| 黑人巨大精品欧美一区二区三区| 日韩暖暖在线视频| 国产欧美 在线欧美| 亚洲91精品在线| 45www国产精品网站| 97视频在线观看免费| 亚洲欧美中文在线视频| 亚洲男人天堂2023| 国产欧美日韩91| 国产欧亚日韩视频| 国产欧美日韩中文| 91精品国产高清久久久久久| 国产精品色悠悠| 国模精品视频一区二区| 亚洲精品福利在线观看| 欧美高清电影在线看| 国产午夜精品视频免费不卡69堂| 欧美日韩精品在线视频| 久久久亚洲国产天美传媒修理工| 国语自产精品视频在线看抢先版图片| 精品成人国产在线观看男人呻吟| 亚洲第一偷拍网| 97超级碰碰碰久久久| 久久精品91久久香蕉加勒比| 国产视频精品免费播放| 欧美性猛交xxxxx水多| 欧美激情成人在线视频| 法国裸体一区二区| 国产精品久久久久一区二区| www.日韩欧美| 久久精品国产成人精品| 秋霞成人午夜鲁丝一区二区三区| 久久韩国免费视频| 高清一区二区三区四区五区| 日本午夜精品理论片a级appf发布| 精品香蕉在线观看视频一| 狠狠躁天天躁日日躁欧美| 国产一区二区三区在线观看视频| 精品视频偷偷看在线观看| 黑人巨大精品欧美一区二区免费| 久久婷婷国产麻豆91天堂| 国产一区二中文字幕在线看| 日韩在线资源网| 久操成人在线视频| 亚洲欧美日韩一区二区三区在线| 欧美性xxxx在线播放| 最新69国产成人精品视频免费| 亚洲美女福利视频网站| 欧美一级电影久久| 亚洲国产一区二区三区在线观看| 欧美美女15p| 日韩视频欧美视频| 久久香蕉国产线看观看av| 久久好看免费视频| 九九视频这里只有精品| 亚洲综合在线中文字幕| 美日韩丰满少妇在线观看| 伊人一区二区三区久久精品| 亚洲精品98久久久久久中文字幕| 日本午夜人人精品| 欧美资源在线观看| 日本视频久久久| 亚洲第一色在线| 91精品视频在线| 久久成人国产精品| 日韩av一区在线观看| 欧美日韩国产成人在线| 亚洲精品视频播放| 亚洲专区在线视频| 欧美激情videos| 国产91精品黑色丝袜高跟鞋| 成人激情av在线| 久久精品99久久香蕉国产色戒| 久久久久免费视频| 久久免费高清视频| 粉嫩av一区二区三区免费野| 精品久久久国产精品999| 亚洲欧美日韩第一区| 国产精品精品一区二区三区午夜版| 尤物九九久久国产精品的特点| 91伊人影院在线播放| 国产aⅴ夜夜欢一区二区三区| 国产精品免费视频久久久| 亚洲精品综合久久中文字幕| 国产成人高潮免费观看精品| 91精品久久久久久久久久久久久| 亚洲成色777777女色窝| 国产va免费精品高清在线| 日产日韩在线亚洲欧美| 亚洲乱码一区av黑人高潮| 国产精品久久久久国产a级| 欧美一区二区视频97| 亚洲美女性视频| 色与欲影视天天看综合网| 91精品国产免费久久久久久| 国产精品第100页| 国产精品欧美日韩一区二区| 永久免费看mv网站入口亚洲| 上原亚衣av一区二区三区| 欧美丰满少妇xxxxx做受| 人人爽久久涩噜噜噜网站| 日韩在线激情视频| 高清欧美性猛交xxxx黑人猛交| 国产日韩欧美综合| 成人观看高清在线观看免费| 国产成人91久久精品| 国产精品日韩欧美| 精品亚洲一区二区| www.亚洲人.com| 国产91成人在在线播放| 成人国产精品久久久久久亚洲| 亚洲电影成人av99爱色| 国产亚洲精品一区二区| 日韩av网站在线| 国产色婷婷国产综合在线理论片a| 久久大大胆人体| 91九色单男在线观看| 亚洲成人精品久久久| 尤物九九久久国产精品的分类| 欧美激情精品久久久久久大尺度| 亚洲国产成人在线播放| 91精品国产成人| 久久影视三级福利片| 精品中文字幕视频| 国产精品高潮视频| 久久久噜久噜久久综合| 国产精品美乳在线观看| 久久成人18免费网站| 按摩亚洲人久久| 成人免费网站在线看| 亚洲аv电影天堂网| 久久久精品视频成人| 国产91精品最新在线播放| 日韩专区在线观看| 欧美日韩亚洲系列| 欧美激情精品久久久久久大尺度| 91国产美女在线观看| 红桃视频成人在线观看| 亚洲欧美成人在线| 国产一区二区三区视频在线观看| 97国产精品免费视频| 久久久久久国产精品| 欧美激情videoshd| 亚洲精品自产拍| 亚洲欧美日韩另类| 久久国内精品一国内精品|