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

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

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

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

從內存模型講起

這里寫圖片描述

由于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
欧美电影在线观看高清| 欧美大片第1页| 狠狠色香婷婷久久亚洲精品| 久久久免费精品| 日韩欧中文字幕| 一区二区三区视频在线| 国产精品都在这里| 国产日韩欧美视频在线| 国产999精品久久久影片官网| 欧美二区乱c黑人| 欧美精品免费在线| 91香蕉嫩草神马影院在线观看| 亚洲国内精品视频| 黑人狂躁日本妞一区二区三区| 成人激情av在线| 久久久久久久爱| 国产精品一区二区久久久| 亚洲福利视频在线| 岛国视频午夜一区免费在线观看| 亚洲激情久久久| 亚洲国产成人精品久久久国产成人一区| 国产成人福利网站| 亚洲精品www久久久久久广东| 日韩av成人在线观看| 亚洲天堂免费观看| 国产999精品久久久| 韩国精品美女www爽爽爽视频| 亚洲美女性生活视频| 中文字幕日韩av综合精品| 欧美视频第一页| 欧美日韩免费看| 亚洲午夜精品久久久久久久久久久久| 在线观看日韩视频| 68精品久久久久久欧美| 欧美性生活大片免费观看网址| 国产精品一区二区三区免费视频| 中文字幕一区电影| 日韩高清av一区二区三区| 亚洲精品午夜精品| 人九九综合九九宗合| 深夜福利亚洲导航| 欧美精品激情在线观看| 亚洲国产精品视频在线观看| 久久伊人精品天天| 日韩69视频在线观看| 欧美人在线视频| 亚洲第一页在线| 国产a级全部精品| 精品国内产的精品视频在线观看| 欧美电影电视剧在线观看| 国内精品一区二区三区| 亚洲第一页中文字幕| 日韩av影视在线| 另类色图亚洲色图| 国产亚洲精品久久久久久777| 欧美成人免费播放| 亚洲欧美国产精品专区久久| 一区二区国产精品视频| 亚洲午夜精品久久久久久久久久久久| 色噜噜狠狠色综合网图区| 国产精品男人爽免费视频1| 奇米成人av国产一区二区三区| 亚洲毛片在线看| 高潮白浆女日韩av免费看| 亚洲视频视频在线| 最好看的2019的中文字幕视频| 亚洲国产成人精品女人久久久| 亚洲成色777777在线观看影院| 国产精品激情av在线播放| 2019中文字幕在线| 欧美日韩国内自拍| 中文字幕欧美日韩精品| 亚洲一区二区三区视频| 欧美激情高清视频| 美日韩精品免费视频| 国产精品吹潮在线观看| 成人黄色短视频在线观看| 91在线精品视频| 日韩欧美第一页| 国产女人18毛片水18精品| www日韩欧美| 欧美多人乱p欧美4p久久| 欧美性猛交xxxxx免费看| 欧美成人精品一区| 亚洲国产成人久久综合一区| 欧美性猛交xxxx黑人猛交| 91超碰caoporn97人人| www高清在线视频日韩欧美| 亚洲电影免费观看高清| 欧美性受xxxx白人性爽| 日韩视频免费在线观看| 成人欧美一区二区三区在线| 日韩中文字幕免费视频| 亚洲影院色无极综合| 欧美性xxxxx| 98精品国产高清在线xxxx天堂| 狠狠综合久久av一区二区小说| 在线观看91久久久久久| 欧美日韩国产精品一区二区不卡中文| 成人亚洲综合色就1024| 久久久久久91香蕉国产| 亚洲美女久久久| 韩国一区二区电影| 国产精国产精品| 青青久久av北条麻妃黑人| 97在线观看视频国产| 久久精品成人欧美大片| 久久视频在线看| 九九九久久国产免费| 精品一区二区三区三区| 亚洲女同精品视频| 欧美成人激情视频| 91精品免费看| 国产一区二区三区直播精品电影| 国产精品电影在线观看| 一区二区三区视频观看| 亚洲人成伊人成综合网久久久| 自拍亚洲一区欧美另类| 91九色视频导航| 欧美交受高潮1| 国产精品麻豆va在线播放| 欧美精品激情blacked18| 久久精品国产91精品亚洲| 在线观看免费高清视频97| 久久久精品在线观看| 国产精品成久久久久三级| 国产精品成久久久久三级| 在线观看不卡av| 91情侣偷在线精品国产| 欧美另类交人妖| 国产精品爱啪在线线免费观看| 日韩免费高清在线观看| 亚洲一区中文字幕在线观看| 成人综合国产精品| 日韩在线视频免费观看高清中文| 国产精品久久久久久久美男| 欧美精品做受xxx性少妇| www.欧美精品一二三区| 亚洲一区二区三区xxx视频| 2020欧美日韩在线视频| 97精品伊人久久久大香线蕉| 欧美激情精品久久久久久蜜臀| 亚洲欧美精品伊人久久| 中文字幕在线国产精品| 久久久久久久久久久人体| 欧美亚洲国产日韩2020| 日韩美女视频在线观看| 91av福利视频| 97精品国产97久久久久久免费| 91香蕉国产在线观看| 中文字幕欧美日韩精品| 国产精品扒开腿爽爽爽视频| 国产精品欧美日韩一区二区| 91九色国产在线| 欧美另类极品videosbest最新版本| 日韩av在线一区二区| 91精品久久久久久久| 亚洲精品国产suv| 日本精品久久久| 欧美大片网站在线观看| 一区国产精品视频| www.亚洲免费视频| 精品呦交小u女在线| 日韩精品www|