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

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

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

2019-11-14 13:07:01
字體:
來源:轉載
供稿:網友

從內存模型講起

這里寫圖片描述

由于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/
上一篇:SFML game fun

下一篇:17 - 05 - 10 C++初窺

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美自拍大量在线观看| 国产男人精品视频| 亚洲韩国日本中文字幕| 亚洲精品一区av在线播放| 538国产精品一区二区免费视频| 亚洲欧美日韩中文视频| 精品久久久精品| 亚洲xxxxx性| 久久露脸国产精品| 欧美怡春院一区二区三区| 亚洲自拍偷拍区| 欧美日韩一区二区免费在线观看| 91高清在线免费观看| 国产一区深夜福利| 亚洲护士老师的毛茸茸最新章节| 尤物精品国产第一福利三区| 伊人久久大香线蕉av一区二区| 日韩电影中文字幕在线| 欧美精品一区在线播放| 欧日韩在线观看| 欧美日本高清一区| 亚洲欧美制服中文字幕| 欧美精品18videos性欧美| 欧美性生交xxxxxdddd| 精品免费在线视频| 日韩国产在线播放| 日韩在线观看你懂的| 精品久久久久人成| www.亚洲人.com| 国内伊人久久久久久网站视频| 日韩欧美黄色动漫| 亚洲色图狂野欧美| 欧美一级在线亚洲天堂| 欧美日韩免费看| 91啪国产在线| 成人天堂噜噜噜| 国产一区二区三区四区福利| 亚洲综合色激情五月| 国产成人免费av| 亚洲激情电影中文字幕| 亚洲欧洲午夜一线一品| 日韩视频一区在线| 国产一区二区日韩精品欧美精品| 国产亚洲aⅴaaaaaa毛片| 欧美在线视频免费观看| 国产不卡av在线免费观看| 亚洲人a成www在线影院| 国产午夜精品久久久| 日韩高清av一区二区三区| 黑人巨大精品欧美一区免费视频| 欧美风情在线观看| 国产一区二区日韩精品欧美精品| 亚洲国产精品成人精品| 国产欧美亚洲视频| 91久久精品国产| 欧美二区在线播放| 色哟哟入口国产精品| 日韩av影院在线观看| 欧美做爰性生交视频| 亚洲国产精品网站| 日韩av一区二区在线观看| 国产婷婷97碰碰久久人人蜜臀| 国产精品国产三级国产aⅴ9色| 成人精品视频久久久久| 久久91亚洲人成电影网站| 亚洲美女精品成人在线视频| 欧美一级黑人aaaaaaa做受| 欧美丝袜一区二区三区| 亚洲欧美国产高清va在线播| 午夜精品一区二区三区av| 久久99久久亚洲国产| 欧美在线观看网址综合| 精品亚洲永久免费精品| 草民午夜欧美限制a级福利片| 91精品在线播放| 91精品国产高清久久久久久| 亚洲精品一区二区在线| 欧美成人中文字幕在线| 国产午夜精品视频| 91午夜理伦私人影院| 亚洲欧洲免费视频| 久久91精品国产91久久跳| 51久久精品夜色国产麻豆| 国产精品夫妻激情| 国产精品成人一区二区| 77777少妇光屁股久久一区| 91精品国产91久久久久福利| 精品美女国产在线| 九九久久久久99精品| 国产精品久久一区| 欧美性感美女h网站在线观看免费| 欧美日韩加勒比精品一区| 热门国产精品亚洲第一区在线| 国产成人+综合亚洲+天堂| 97视频在线看| 1769国内精品视频在线播放| 亚洲国产精品一区二区久| 国产精品久久久久久久av大片| 国产综合久久久久久| 国产精品青草久久久久福利99| 久久精品视频亚洲| 国产精品丝袜一区二区三区| 色哟哟亚洲精品一区二区| 成人免费观看网址| 久久久久久中文字幕| 国产精品美腿一区在线看| 国产日韩欧美中文在线播放| 亚洲精选中文字幕| 51视频国产精品一区二区| 亚洲性生活视频| 狠狠色狠色综合曰曰| 欧美大片在线影院| 欧美激情一级精品国产| 国产精品露脸自拍| 91性高湖久久久久久久久_久久99| 久久久久北条麻妃免费看| 欧美成人性色生活仑片| 精品久久久久久久久久国产| 日本精品久久久久影院| 欧美亚洲第一页| 国产欧美日韩最新| 91亚洲精品一区二区| 2019亚洲男人天堂| 日韩在线观看免费| 欧美日韩国产成人在线观看| 国产精品福利在线| 久久久久亚洲精品成人网小说| 国产伦精品一区二区三区精品视频| 欧美性猛xxx| 国产成人自拍视频在线观看| 亚洲欧美在线x视频| 欧美成人午夜激情视频| 国产精品中文字幕在线观看| 欧美性生交大片免网| 欧美性xxxxx极品| 日韩国产精品一区| 国产成人亚洲综合91精品| 久久人体大胆视频| 久久99国产精品自在自在app| 久久久久久久久电影| 国产玖玖精品视频| 久久久久久久久久久免费精品| 国产精品白丝av嫩草影院| 26uuu另类亚洲欧美日本老年| 日韩av在线高清| 国产91在线播放九色快色| 午夜欧美不卡精品aaaaa| 最近中文字幕mv在线一区二区三区四区| 久久夜色精品国产亚洲aⅴ| 91精品综合久久久久久五月天| 九色精品美女在线| 黑人巨大精品欧美一区二区三区| 国产亚洲成av人片在线观看桃| 国产97人人超碰caoprom| 亚洲人成毛片在线播放| 黑人精品xxx一区一二区| 热久久免费视频精品| 亚洲国产欧美一区| 揄拍成人国产精品视频| 91久久久久久久一区二区| 日韩欧美中文字幕在线播放| 国产精品久久一区| 国产精品福利观看| 欧美精品video|