volatile關(guān)鍵字修飾對象是什么效果?

如果volatile的修飾的是一個引用類型的對象變量,那么對象中定義的一些普通全局變量是否會受到volatile關(guān)鍵字的效果影響呢毛萌?”

接下來,我們就一起來分析下這個問題!讓我們先通過一個例子來回顧下volatile關(guān)鍵字的作用喝滞!

public class VolatitleFoo {
    //類變量
    final static int max = 5;
    static int init_value = 0;

    public static void main(String args[]) {
        //啟動一個線程朝聋,當(dāng)發(fā)現(xiàn)local_value與init_value不同時,則輸出init_value被修改的值
        new Thread(() -> {
            int localValue = init_value;
            while (localValue < max) {
                if (init_value != localValue) {
                    System.out.printf("The init_value is update ot [%d]\n", init_value);
                    //對localValue進(jìn)行重新賦值
                    localValue = init_value;
                }
            }
        }, "Reader").start();
        //啟動updater線程囤躁,主要用于對init_value的修改,當(dāng)local_value=5的時候退出生命周期
        new Thread(() -> {
            int localValue = init_value;
            while (localValue < max) {
                //修改init_value
                System.out.printf("The init_value will be changed to [%d]\n", ++localValue);
                init_value = localValue;
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Updater").start();
    }
}

在上面的代碼示例中荔睹,我們定義了兩個類變量max狸演、init_value,然后在主線程中分別啟動一個Reader線程僻他,一個Updater線程宵距。Updater線程做的事情就是在值小于max的值時以每兩毫秒的速度進(jìn)行自增。而Reader線程則是在感知init_value值發(fā)生變化的情況下進(jìn)行讀取操作吨拗。

期望的效果是線程Updater更新init_value值之后满哪,可以立刻被線程Reader感知到婿斥,從而進(jìn)行輸出顯示。實際運(yùn)行效果如下:

The init_value will be changed to [1]
The init_value will be changed to [2]
The init_value will be changed to [3]
The init_value will be changed to [4]
The init_value will be changed to [5]

實際的運(yùn)行效果是在Updater修改類變量init_value后哨鸭,Reader線程并沒有立馬感知到變化民宿,所以沒有進(jìn)行相應(yīng)的顯示輸出。而原因就在于共享類變量init_value在被線程Updater拷貝到該線程的工作內(nèi)存中后像鸡,Updater對變量init_value的修改都是在工作內(nèi)存中進(jìn)行的活鹰,完成操作后沒有立刻同步回主內(nèi)存,所以Reader線程對其改變并不可見只估。

為了解決線程間對類變量init_value的可見性問題志群,我們將類變量init_value用volatile關(guān)鍵字進(jìn)行下修飾,如下:

static volatile int init_value = 0;

然后我們再運(yùn)行下代碼蛔钙,看看結(jié)果:

The init_value will be changed to [1]
The init_value is update ot [1]
The init_value will be changed to [2]
The init_value is update ot [2]
The init_value will be changed to [3]
The init_value is update ot [3]
The init_value will be changed to [4]
The init_value is update ot [4]
The init_value will be changed to [5]
The init_value is update ot [5]

此時線程Updater對類變量init_value的修改锌云,立馬就能被Reader線程感知到了,這就是volatile關(guān)鍵字的效果吁脱,可以讓共享變量在線程間實現(xiàn)可見桑涎,原因就在于在JVM的語義層面要求被volatile修飾的共享變量,在工作內(nèi)存中的修改要立刻同步回主內(nèi)存豫喧,并且讀取也需要每次都重新從主內(nèi)存中刷新一份到工作內(nèi)存中后才可以操作石洗。

關(guān)于以上適用volatile關(guān)鍵字修飾基本類型的類變量、實例變量的場景紧显,相信大家會比較好理解讲衫。接下來,我們來繼續(xù)改造下代碼:

public class VolatileEntity {
    //使用volatile修飾共享資源i
    //類變量
    final static int max = 5;
    int init_value = 0;
    public static int getMax() {
        return max;
    }
    public int getInit_value() {
        return init_value;
    }
    public void setInit_value(int init_value) {
        this.init_value = init_value;
    }
    private static class VolatileEntityHolder {
        private static VolatileEntity instance = new VolatileEntity();
    }
    public static VolatileEntity getInstance() {
        return VolatileEntityHolder.instance;
    }
}

我們將之前代碼中的類變量init_value放到實體類VolatileEntity中孵班,并將其設(shè)計為一個實例變量涉兽,為了便于理解,我們將實體類VolatileEntity設(shè)計為單例模式篙程,確保兩個線程操作的是同一個共享堆內(nèi)存對象枷畏。如下:

public class VolatileEntityTest {

    //使用volatile修飾共享資源
    private static VolatileEntity volatileEntity = VolatileEntity.getInstance();

    private static final CountDownLatch latch = new CountDownLatch(10);

    public static void main(String args[]) throws InterruptedException {
        //啟動一個線程,當(dāng)發(fā)現(xiàn)local_value與init_value不同時虱饿,則輸出init_value被修改的值
        new Thread(() -> {
            int localValue = volatileEntity.init_value;
            while (localValue < VolatileEntity.max) {
                if (volatileEntity.init_value != localValue) {
                    System.out.printf("The init_value is update ot [%d]\n", volatileEntity.init_value);
                    //對localValue進(jìn)行重新賦值
                    localValue = volatileEntity.init_value;
                }
            }
        }, "Reader").start();

        //啟動updater線程拥诡,主要用于對init_value的修改,當(dāng)local_value=5的時候退出生命周期
        new Thread(() -> {
            int localValue = volatileEntity.init_value;
            while (localValue < VolatileEntity.max) {
                //修改init_value
                System.out.printf("The init_value will be changed to [%d]\n", ++localValue);
                volatileEntity.init_value = localValue;
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Updater").start();
    }
}

在上述代碼中線程Updater和Reader此時操作的是類變量VolatileEntity對象中的普通實例變量init_value氮发。在VolatileEntity對象沒被volatile關(guān)鍵字修飾之前渴肉,我們看下運(yùn)行效果:

The init_value will be changed to [1]
The init_value will be changed to [2]
The init_value will be changed to [3]
The init_value will be changed to [4]
The init_value will be changed to [5]

與未被volatile修飾的int類型的類變量效果一樣,線程Updater對VolatileEntity對象中init_value變量的操作也不能立馬被線程Reader可見爽冕。如果此時我們不VolatileEntity類中單獨用volatile關(guān)鍵字修飾init_value變量仇祭,而是直接VolatileEntity對象用volatile關(guān)鍵字修飾,效果會如何呢颈畸?

private static volatile VolatileEntity volatileEntity = VolatileEntity.getInstance();

此時VolatileEntity對象的引用變量被volatile關(guān)鍵字修飾了乌奇,然而其中的普通實例變量init_value并沒有直接被volatile關(guān)鍵字修飾没讲,然后我們在運(yùn)行下代碼看看效果:

The init_value will be changed to [1]
The init_value is update ot [1]
The init_value will be changed to [2]
The init_value is update ot [2]
The init_value will be changed to [3]
The init_value is update ot [3]
The init_value will be changed to [4]
The init_value is update ot [4]
The init_value will be changed to [5]
The init_value is update ot [5]

從實際的運(yùn)行效果上看,雖然我們沒有直接用volatile關(guān)鍵字修飾對象中的類變量init_value礁苗,而是修改了對象的引用爬凑,但是我們看到對象中的普通實例變量仍然實行了線程間的可見性,也就是說間接也相當(dāng)于被volatile關(guān)鍵字修飾了寂屏。所以贰谣,在這里問題也就基本上有了答案,那就是:“被volatile關(guān)鍵字修飾的對象作為類變量或?qū)嵗兞繒r迁霎,其對象中攜帶的類變量和實例變量也相當(dāng)于被volatile關(guān)鍵字修飾了”吱抚。

這個問題主要是考查大家對volatile關(guān)鍵字的理解是否深入,另外也是對Java數(shù)據(jù)存儲結(jié)構(gòu)的考查考廉,雖然可能大家對volatile關(guān)鍵字的作用會有了解秘豹,但是如果突然被問到這樣的問題,如果不加以思考昌粤,在面試中也是很容易被問懵的既绕!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市涮坐,隨后出現(xiàn)的幾起案子凄贩,更是在濱河造成了極大的恐慌,老刑警劉巖袱讹,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疲扎,死亡現(xiàn)場離奇詭異,居然都是意外死亡捷雕,警方通過查閱死者的電腦和手機(jī)椒丧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來救巷,“玉大人壶熏,你說我怎么就攤上這事∑忠耄” “怎么了棒假?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長精盅。 經(jīng)常有香客問我帽哑,道長,這世上最難降的妖魔是什么渤弛? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮甚带,結(jié)果婚禮上她肯,老公的妹妹穿的比我還像新娘佳头。我一直安慰自己,他們只是感情好晴氨,可當(dāng)我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布康嘉。 她就那樣靜靜地躺著,像睡著了一般籽前。 火紅的嫁衣襯著肌膚如雪亭珍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天枝哄,我揣著相機(jī)與錄音肄梨,去河邊找鬼。 笑死挠锥,一個胖子當(dāng)著我的面吹牛众羡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蓖租,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼粱侣,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蓖宦?” 一聲冷哼從身側(cè)響起齐婴,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎稠茂,沒想到半個月后柠偶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡主慰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年嚣州,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片共螺。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡该肴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出藐不,到底是詐尸還是另有隱情匀哄,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布雏蛮,位于F島的核電站涎嚼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏挑秉。R本人自食惡果不足惜法梯,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧立哑,春花似錦夜惭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至捂掰,卻和暖如春敢会,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背这嚣。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工鸥昏, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人疤苹。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓互广,卻偏偏與公主長得像,于是被迫代替她去往敵國和親卧土。 傳聞我的和親對象是個殘疾皇子惫皱,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內(nèi)容