国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

談?wù)凧ava中Volatile關(guān)鍵字的理解

2019-11-26 14:28:04
字體:
供稿:網(wǎng)友

volatile這個(gè)關(guān)鍵字可能很多朋友都聽說過,或許也都用過。在Java 5之前,它是一個(gè)備受爭議的關(guān)鍵字,因?yàn)樵诔绦蛑惺褂盟鶗?dǎo)致出人意料的結(jié)果。在Java 5之后,volatile關(guān)鍵字才得以重獲生機(jī)。volatile關(guān)鍵字雖然從字面上理解起來比較簡單,但是要用好不是一件容易的事情。

一、前言

  JMM提供了volatile變量定義、final、synchronized塊來保證可見性。
  用volatile修飾的變量,線程在每次使用變量的時(shí)候,都會讀取變量修改后的最的值。volatile很容易被誤用,用來進(jìn)行原子性操作。寫了幾個(gè)測試的例子,大家可以試一試。

二、主程序

public class Main{public static void main(String[] args) throws InterruptedException{List<Thread> threadList = new ArrayList<Thread>();for(int i=0; i<10; ++i){Thread thread = new Thread(new Runnable() {@Overridepublic void run() {Single.Holder.instance.add();}});threadList.add(thread);thread.start();}for(Thread thread : threadList)thread.join();System.out.println(Single.Holder.instance.x);}}

三、單例模式測試

  1、沒有volatile,沒有synchronized的情況   

class Single{public int x = 0;public void add(){try {TimeUnit.MILLISECONDS.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}++this.x;}public static class Holder{public static Single instance = new Single();}} 

    輸出結(jié)果:8, 9, 10都出現(xiàn)過。可以多運(yùn)行,多試一試,就會發(fā)現(xiàn)不同的結(jié)果。

  2、有volatile,沒有synchronized

class Single{public volatile int x = 0;public void add(){try {TimeUnit.MILLISECONDS.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}++this.x;}public static class Holder{public static Single instance = new Single();}}

    輸出結(jié)果:最多出現(xiàn)的是9 和 10。

  3、沒有volatile,有synchronized

class Single{public int x = 0;public synchronized void add(){try {TimeUnit.MILLISECONDS.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}++this.x;}public static class Holder{public static Single instance = new Single();}}

  輸出結(jié)果:無論運(yùn)行多少次都是10。

四、關(guān)于volatile在DCL(double check lock)中的應(yīng)用

public class LazySingleton {private int someField;private static LazySingleton instance;private LazySingleton() {this.someField = new Random().nextInt(200)+1; // (1)}public static LazySingleton getInstance() {if (instance == null) { // (2)synchronized(LazySingleton.class) { // (3)if (instance == null) { // (4)instance = new LazySingleton(); // (5)}}}return instance; // (6)}public int getSomeField() {return this.someField; // (7)}}

  首先說明一下,為什么這種寫法在java中是行不通的!

  假設(shè)線程Ⅰ是初次調(diào)用getInstance()方法,緊接著線程Ⅱ也調(diào)用了getInstance()方法和getSomeField()方法,我們要說明的是線程Ⅰ的語句(1)并不happen-before線程Ⅱ的語句(7)。線程Ⅱ在執(zhí)行g(shù)etInstance()方法的語句(2)時(shí),由于對instance的訪問并沒有處于同步塊中,因此線程Ⅱ可能觀察到也可能觀察不到線程Ⅰ在語句(5)時(shí)對instance的寫入,也就是說instance的值可能為空也可能為非空。我們先假設(shè)instance的值非空,也就觀察到了線程Ⅰ對instance的寫入,這時(shí)線程Ⅱ就會執(zhí)行語句(6)直接返回這個(gè)instance的值,然后對這個(gè)instance調(diào)用getSomeField()方法,該方法也是在沒有任何同步情況被調(diào)用,因此整個(gè)線程Ⅱ的操作都是在沒有同步的情況下調(diào)用 ,這說明線程Ⅰ的語句(1)和線程Ⅱ的語句(7)之間并不存在happen-before關(guān)系,這就意味著線程Ⅱ在執(zhí)行語句(7)完全有可能觀測不到線程Ⅰ在語句(1)處對someFiled寫入的值,這就是DCL的問題所在。很荒謬,是吧?DCL原本是為了逃避同步,它達(dá)到了這個(gè)目的,也正是因?yàn)槿绱耍罱K受到懲罰,這樣的程序存在嚴(yán)重的bug,雖然這種bug被發(fā)現(xiàn)的概率絕對比中彩票的概率還要低得多,而且是轉(zhuǎn)瞬即逝,更可怕的是,即使發(fā)生了你也不會想到是DCL所引起的。

  我的理解是:線程I 和線程II 都有自己的工作存儲,線程I 創(chuàng)建好了instance后,向內(nèi)存刷新的時(shí)間是不確定的,所以線程Ⅱ在執(zhí)行語句(7)完全有可能觀測不到線程Ⅰ在語句(1)處對someFiled寫入的值。

  那么由于在java 5中多增加了一條happen-before規(guī)則:

•對volatile字段的寫操作happen-before后續(xù)的對同一個(gè)字段的讀操作。

  利用這條規(guī)則我們可以將instance聲明為volatile,即: private volatile static LazySingleton instance;
  根據(jù)這條規(guī)則,我們可以得到,線程Ⅰ的語句(5) -> 語線程Ⅱ的句(2) (也就是線程),根據(jù)單線程規(guī)則,線程Ⅰ的語句(1) -> 線程Ⅰ的語句(5)和語線程Ⅱ的句(2) -> 語線程Ⅱ的句(7),再根據(jù)傳遞規(guī)則就有線程Ⅰ的語句(1) -> 語線程Ⅱ的句(7),這表示線程Ⅱ能夠觀察到線程Ⅰ在語句(1)時(shí)對someFiled的寫入值,程序能夠得到正確的行為。

  補(bǔ)充:在java5之前對final字段的同步語義和其它變量沒有什么區(qū)別,在java5中,final變量一旦在構(gòu)造函數(shù)中設(shè)置完成(前提是在構(gòu)造函數(shù)中沒有泄露this引用),其它線程必定會看到在構(gòu)造函數(shù)中設(shè)置的值。而DCL的問題正好在于看到對象的成員變量的默認(rèn)值,因此我們可以將LazySingleton的someField變量設(shè)置成final,這樣在java5中就能夠正確運(yùn)行了。

以上內(nèi)容是小編給大家介紹的Java中Volatile關(guān)鍵字的知識,希望對大家有所幫助!

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 赤壁市| 固阳县| 庆阳市| 临猗县| 花垣县| 庄河市| 丘北县| 桐柏县| 利津县| 寻甸| 和林格尔县| 台北市| 眉山市| 咸宁市| 抚宁县| 阿拉善左旗| 无棣县| 拜城县| 沙坪坝区| 满洲里市| 吐鲁番市| 天门市| 都匀市| 鸡西市| 德格县| 淳化县| 中西区| 蕉岭县| 新邵县| 遂平县| 梁平县| 安溪县| 怀来县| 杂多县| 江安县| 原平市| 拜城县| 呼和浩特市| 太康县| 株洲市| 普宁市|