一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務(wù)器之家 - 編程語(yǔ)言 - JAVA教程 - 談?wù)凧ava中Volatile關(guān)鍵字的理解

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

2020-04-14 11:32小眼兒 JAVA教程

volatile這個(gè)關(guān)鍵字可能很多朋友都聽(tīng)說(shuō)過(guò),或許也都用過(guò)。在Java 5之前,它是一個(gè)備受爭(zhēng)議的關(guān)鍵字,因?yàn)樵诔绦蛑惺褂盟鶗?huì)導(dǎo)致出人意料的結(jié)果,本文給大家介紹java中volatile關(guān)鍵字,需要的朋友參考下

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

一、前言

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

二、主程序

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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() {
@Override
public void run() {
Single.Holder.instance.add();
}
});
threadList.add(thread);
thread.start();
}
for(Thread thread : threadList)
thread.join();
System.out.println(Single.Holder.instance.x);
}
}

三、單例模式測(cè)試

  1、沒(méi)有volatile,沒(méi)有synchronized的情況   

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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)過(guò)。可以多運(yùn)行,多試一試,就會(huì)發(fā)現(xiàn)不同的結(jié)果。

  2、有volatile,沒(méi)有synchronized

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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、沒(méi)有volatile,有synchronized

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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é)果:無(wú)論運(yùn)行多少次都是10。

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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)
}
}

  首先說(shuō)明一下,為什么這種寫(xiě)法在java中是行不通的!

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

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

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

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

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

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

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日韩精品国产自在欧美 | 99视频精品免费99在线 | 乌克兰成人性色生活片 | 日本在线观看www鲁啊鲁视频 | 亚洲精品久久久成人 | 涩涩成人| 国产 国语对白 露脸正在播放 | 亚洲 欧美 日本 国产 高清 | 红色一片在线影视 | 紧身裙女教师波多野结衣 | 俄罗斯一级毛片免费播放 | 国产亚洲精品福利在线 | 久久永久免费视频 | 午夜国产在线观看 | 国产精品美女福利视频免费专区 | aaaa黄 | 免费91麻豆精品国产自产在线观看 | 天堂素人在线 | 国产亚洲欧美日韩俺去了 | 男人j放进女人的p视频免费 | 色综合网亚洲精品久久 | 亚洲人成伊人成综合网久久 | 99 久久99久久精品免观看 | 四虎在线免费播放 | 美女口述又粗又大感觉 | 久久人妻无码毛片A片麻豆 久久热这里只有 精品 | 国产欧美日韩图片一区二区 | 人人精品久久 | 久久婷婷五月免费综合色啪 | 韩国久播影院理论片不卡影院 | 欧美国产日韩1区俺去了 | 韩日理论片 | 国产一卡2卡3卡四卡国色天香 | 欧美一区二区三区四区在线观看 | 9久久9久久精品 | 香港三级血恋3 | 精品精品国产yyy5857香蕉 | 亚洲天堂在线视频观看 | 天天久久影视色香综合网 | 成人精品mv视频在线观看 | 国产福利资源 |