在一些開源的框架的源碼當中時不時都可以看到volatile這個關鍵字,最近特意學習一下volatile關鍵字的使用方法效诅。
很多資料中是這樣介紹volatile關鍵字的:
volatile是輕量級的synchronized,它在多處理器開發(fā)中保證了共享變量的“可見性”惨奕∨置耄可見性的意思是當一個線程修改一個共享變量時,另外一個線程能讀到這個修改的值惋增。
文字不太好理解,通過例子來理解改鲫。
1诈皿、例子
首先看一個沒有使用volatile關鍵字例子:
package com.swnote.java;/**
* volatile測試例子
*
* @author lzj
* @date [2019-04-47]
*/publicclassVolatileTest{private boolean flag;? ? public staticvoidmain(String[] args) {? ? ? ? VolatileTest test =newVolatileTest();? ? ? ? test.test();? ? }? ? publicvoidtest() {newThread(() -> {try{? ? ? ? ? ? ? ? Thread.sleep(1000L);? ? ? ? ? ? }catch(InterruptedException e) {? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? }? ? ? ? ? ? flag =true;? ? ? ? }).start();newThread(() -> {while(true) {if(flag) {? ? ? ? ? ? ? ? ? ? System.out.println("thread flag = "+ flag);? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }).start();? ? }}
該例子中定義了一個flag共享變量,test方法里面開啟了兩個線程像棘,第一個線程在等待1秒中后修改共享變量flag的值為true稽亏,第二個線程通過循環(huán)判斷flag的值,當flag的值為true時缕题,輸出內容截歉。
此時有兩種猜想:
執(zhí)行后,可以看到輸出內容避除,即說明第二個線程能夠感知到第一個線程對共享變量flag的修改
執(zhí)行后怎披,沒有任務內容,即說明第二個線程無法感知到第一個線程對共享變量flag的修改
然后執(zhí)行結果為:
沒有任務的輸出內容瓶摆,即證明了此時這樣情況下第二個線程無法感知到第一個線程對共享變量flag的修改的
現(xiàn)在修改一下例子凉逛,即為flag變量加上volatile關鍵字,即:
privatevolatilebooleanflag;
然后再運行群井,此時結果為:
此時就有內容輸出了状飞,說明加上volatile關鍵字后,第二個線程可以感知到第一個線程對共享變量flag的修改的,這就是上面概念中所說的volatile在多處理器開發(fā)中保證了共享變量的“可見性”诬辈。
進群:697699179可以獲取Java各類入門學習資料酵使!
這是我的微信公眾號【編程study】各位大佬有空可以關注下,每天更新Java學習方法焙糟,感謝口渔!
學習中遇到問題有不明白的地方,推薦加小編Java學習群:697699179內有視頻教程 穿撮,直播課程 缺脉,等學習資料,期待你的加入
2悦穿、原理
通過上面的例子證明了volatile在多處理器開發(fā)中保證了共享變量的“可見性”攻礼,那它是怎么實現(xiàn)的呢?
這時就得介紹一下Java的內存模型了栗柒,首先看如下示意圖:
Java內存模型是如上面所示的:
共享變量存儲在主內存中礁扮,每個線程都有一個私有的本地內存,本地內存保存了被該線程使用到的主內存的副本拷貝瞬沦,線程對變量的所有操作都必須在自己的本地內存中進行太伊,而不能直接讀寫主內存中的變量。
根據(jù)此理解蛙埂,上述例子的在沒有加volatile時的情況是這樣的:
第一個線程從主內存中獲取共享變量flag的值倦畅,此時值為false遮糖,將該值放到自己的本地內存中绣的,然后對變量進行修改,將值改為true欲账,此時也只是將本地內存中flag的值改為了true屡江,此時還沒有將值同步到主內存中,然后第二線程也是將共享變量flag的值放到自己的本地內存中赛不,而此時flag的值還是為false惩嘉,所以就是一直沒有內容輸出了。
然而加上volatile關鍵字后踢故,第一個線程對flag的修改會強制刷新到主內存中去文黎,同時還會導致其他線程中的本地內存的值會無效,需要重新到主內存獲取殿较,這樣就保證了第一個線程對flag修改后耸峭,第二線程能夠感知到。
3淋纲、注意點
volatile是輕量級的synchronized劳闹,但是它是不能夠代替synchronized的,因為volatile只能保證原子性操作的安全,對于復合操作本涕,volatile是不能保證線程安全的业汰。
例如:
packagecom.swnote.java;/** * 復合操作例子 * *@authorlzj *@date[2019-04-27] */publicclassStatisticTest{privatevolatileintnum =0;publicstaticvoidmain(String[] args){? ? ? ? StatisticTest test =newStatisticTest();? ? ? ? test.statistic();? ? }publicvoidstatistic(){for(inti =0; i <20; i++) {newThread(() -> {? ? ? ? ? ? ? ? num++;? ? ? ? ? ? }).start();? ? ? ? }? ? ? ? System.out.println("num = "+ num);? ? }}
期望的運行結果是20,可是幾乎每次運行結果都是不一樣的菩颖,例如有的結果為:
這是因為num++這個操作不是原子性的样漆,所以即使使用了volatile關鍵字,也是不能保證安全的晦闰。