最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java關(guān)鍵字volatile詳析

 更新時間:2022年01月26日 08:45:34   作者:負(fù)債程序猿?  
這篇文章主要介紹了Java關(guān)鍵字volatile,volatile關(guān)鍵字可以說是Java虛擬機提供的最輕量級的同步機制,但對于為什么它只能保證可見性,不保證原子性,它又是如何禁用指令重排的,還有很多同學(xué)沒徹底理解,文章會讓大家牢掌握一個Java核心知識點

volatile關(guān)鍵字關(guān)于先說它的兩個作用:

  • 保證變量在內(nèi)存中對線程的可見性
  • 禁用指令重排

每個字都認(rèn)識,湊在一起就麻了

這兩個作用通常很不容易被我們Java開發(fā)人員正確、完整地理解,以至于許多同學(xué)不能正確地使用volatile

一、可見性

碼:

public class VolatileTest {
? ? private static volatile int count = 0;

? ? private static void increase() {
? ? ? ? count++;
? ? }

? ? public static void main(String[] args) throws InterruptedException {
? ? ? ? for (int i = 0; i < 10; i++) {
? ? ? ? ? ? new Thread(() -> {
? ? ? ? ? ? ? ? for (int j = 0; j < 10000; j++) {
? ? ? ? ? ? ? ? ? ? increase();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }).start();
? ? ? ? }
?? ??? ?// 所有線程累加完成后輸出
? ? ? ? while (Thread.activeCount() > 2) Thread.yield();
? ? ? ? System.out.println(count);
? ? }
}

代碼很好理解,開了十個線程對同一個共享變量count做累加,每個線程累加1w次

count我們已經(jīng)用volatile修飾,已經(jīng)保證了count對十個線程在內(nèi)存中的可見性,按理說十個線程執(zhí)行完畢count的值應(yīng)該10w

運行多次,結(jié)果都遠(yuǎn)小于期望值

是哪個環(huán)節(jié)出了問題?

你肯定聽過一句話:volatile只保證可見性,不保證原子性

這句話就是答案,但是依舊很多人沒搞懂其中的奧秘

說來話長我長話短說,簡單來講就是 count++這個操作不是原子的,它是分三步進(jìn)行

  • 從內(nèi)存讀取 count 的值
  • 執(zhí)行 count + 1
  • 將 count 的新值寫回

要徹底搞懂這個問題,我們得從字節(jié)碼入手

下面是increase方法編譯后的字節(jié)碼

看不懂沒關(guān)系,我們一行一行來看:

  • GETSTATIC:讀取 count 的當(dāng)前值
  • ICONST_1:將常量 1 加載到棧頂
  • IADD:執(zhí)行+1
  • PUTSTATIC:寫入count最新值

ICONST_1IADD其實就是真正的++操作

關(guān)鍵點來了,volatile只能保證線程在GETSTATIC這一步拿到的值是最新的,但當(dāng)該線程執(zhí)行到下面幾行指令時,這期間可能就有其它線程把count的值修改了,最終導(dǎo)致舊值把真正的新值覆蓋

所以,并發(fā)編程中,只靠volatile修飾共享變量是不可靠的,最終還是要通過對關(guān)鍵方法加鎖來保證線程安全

就如上面的demo,稍加修改就能實現(xiàn)真正的線程安全

最簡單的,給increase方法加個synchronized (synchronized怎么實現(xiàn)線程安全的我就不啰嗦了,我以前講過 synchronized底層實現(xiàn)原理

? ? private synchronized static void increase() {
? ? ? ? ++count;
? ? }

run幾下:

這不就妥了嘛

到現(xiàn)在,對于以下兩點你應(yīng)該有了新的認(rèn)知:

  • volatile保證變量在內(nèi)存中對線程的可見性
  • volatile只保證可見性,不保證原子性

二、關(guān)于指令重排

并發(fā)編程中,cpu自身和虛擬機為了提高執(zhí)行效率,都會采用指令重排(在保證不影響結(jié)果的前提下,將某些代碼亂序執(zhí)行)

  • 關(guān)于cpu:為了從分利用cpu,實際執(zhí)行指令時會做優(yōu)化;
  • 關(guān)于虛擬機:在HotSpot vm中,為了提升執(zhí)行效率,JIT(即時編譯)模式也會做指令優(yōu)化

指令重排在大部分場景下確實能提升執(zhí)行效率,但有些場景對代碼執(zhí)行順序是強依賴的,此時我們需要禁用指令重排,如下面這個場景

偽代碼取自《深入理解Java虛擬機》:

其描述的場景是開發(fā)中常見配置讀取過程,只是我們在處理配置文件時一般不會出現(xiàn)并發(fā),所以沒有察覺這會有問題。
試想一下,如果定義initialized變量時沒有使用volatile修飾,就可能會由于指令重排序的優(yōu)化,導(dǎo)致位于線程A中最后一條代碼“initialized=true”被提前執(zhí)行(這里雖然使用Java作為偽代碼,但所指的重排序優(yōu)化是機器級的優(yōu)化操作,提前執(zhí)行是指這條語句對應(yīng)的匯編代碼被提前執(zhí)行),這樣在線程B中使用配置信息的代碼就可能出現(xiàn)錯誤,而volatile通過禁止指令重排則可以避免此類情況發(fā)生

禁用指令重排只需要將變量聲明為volatile,是不是很神奇

我們來看看volatile是如何實現(xiàn)禁用指令重排的:

這是個單例模式的實現(xiàn),下面是它的部分字節(jié)碼,紅框中 mov%eax,0x150(%esi) 是對instance賦值

可以看到,在賦值后,還執(zhí)行了 lock addl$0x0,(%esp) 指令,關(guān)鍵點就在這兒,這行指令相當(dāng)于此處設(shè)置了個 內(nèi)存屏障 ,有了內(nèi)存屏障后,cpu或虛擬機在指令重排時就不能把內(nèi)存屏障后面的指令提前到內(nèi)存屏障前面,好好捋一下這段話

到此這篇關(guān)于Java關(guān)鍵字volatile詳析的文章就介紹到這了,更多相關(guān)Java關(guān)鍵字volatile內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JAVA解決在@autowired,@Resource注入為null的情況

    JAVA解決在@autowired,@Resource注入為null的情況

    這篇文章主要介紹了JAVA解決在@autowired,@Resource注入為null的情況,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • 詳解Spring 中 Bean 的生命周期

    詳解Spring 中 Bean 的生命周期

    這篇文章主要介紹了Spring 中 Bean 的生命周期的相關(guān)資料,幫助大家更好的理解和使用spring框架,感興趣的朋友可以了解下。
    2021-01-01
  • Java forEach對原數(shù)組的操作過程

    Java forEach對原數(shù)組的操作過程

    forEach對于基本數(shù)據(jù)類型,是直接賦值,對于引用數(shù)據(jù)類型,是引用地址值,forEach遍歷時,是創(chuàng)建的臨時變量,引用的數(shù)據(jù)地址,本文給大家介紹Java forEach對原數(shù)組的操作過程,感興趣的朋友一起看看吧
    2024-02-02
  • 詳解Spring Data JPA動態(tài)條件查詢的寫法

    詳解Spring Data JPA動態(tài)條件查詢的寫法

    本篇文章主要介紹了Spring Data JPA動態(tài)條件查詢的寫法 ,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • 詳解Eclipse 字體、字號的設(shè)置、最佳字體推薦

    詳解Eclipse 字體、字號的設(shè)置、最佳字體推薦

    這篇文章主要介紹了Eclipse 字體、字號的設(shè)置、最佳字體推薦,需要的朋友可以參考下
    2020-09-09
  • java實現(xiàn)客房管理系統(tǒng)

    java實現(xiàn)客房管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java實現(xiàn)客房管理系統(tǒng),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-02-02
  • Java中CountDownLatch用法解析

    Java中CountDownLatch用法解析

    這篇文章主要為大家詳細(xì)介紹了Java中CountDownLatch用法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • 設(shè)置JavaScript自動提示-Eclipse/MyEclipse

    設(shè)置JavaScript自動提示-Eclipse/MyEclipse

    自動提示需要2個組件,分別是:ext-4.0.2a.jsb2||spket-1.6.16.jar,需要的朋友可以參考下
    2016-05-05
  • Spring中的@RefreshScope注解作用

    Spring中的@RefreshScope注解作用

    這篇文章主要介紹了Spring中的@RefreshScope注解作用詳解,@RefreshScope注解是Spring Cloud中的一個重要注解,用于實現(xiàn)動態(tài)刷新配置的功能,當(dāng)我們在應(yīng)用程序中使用@Value注解獲取配置屬性時,如果配置發(fā)生變化,需要重啟應(yīng)用程序才能生效,需要的朋友可以參考下
    2023-10-10
  • 詳細(xì)總結(jié)各種排序算法(Java實現(xiàn))

    詳細(xì)總結(jié)各種排序算法(Java實現(xiàn))

    下面小編就為大家?guī)硪黄敿?xì)總結(jié)各種排序算法(Java實現(xiàn))。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09

最新評論

花莲县| 高密市| 屏边| 华宁县| 石渠县| 塔城市| 墨竹工卡县| 阳西县| 青浦区| 谢通门县| 上饶县| 乌兰县| 南漳县| 长岭县| 东安县| 鹤山市| 故城县| 广昌县| 常山县| 天全县| 深州市| 兴国县| 平潭县| 朝阳县| 怀安县| 镇康县| 波密县| 墨玉县| 衡水市| 从江县| 象州县| 遵义市| 丹江口市| 昌邑市| 元氏县| 新野县| 灵川县| 砀山县| 沂水县| 平潭县| 扎囊县|