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

Java多線程的原子性,可見性,有序性你都了解嗎

 更新時間:2022年03月02日 09:16:21   作者:小小茶花女  
這篇文章主要為大家詳細介紹了Java多線程的原子性,可見性,有序性,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助

問題:

1.什么是原子性、可見性、有序性?

1. 原子性問題

原子性、可見性、有序性是并發(fā)編程所面臨的三大問題。

所謂原子操作,就是“不可中斷的一個或一系列操作”,是指不會被線程調(diào)度機制打斷的操作。這種操作一旦開始,就一直運行到結(jié)束,中間不會有任何線程的切換。

例如對于 i++ 而言,實際會產(chǎn)生如下的 JVM 字節(jié)碼指令:

getstatic i  // 獲取靜態(tài)變量i的值(內(nèi)存取值)
iconst_1     // 準(zhǔn)備常量1
iadd         // 自增 (寄存器增加1)
putstatic i  // 將修改后的值存入靜態(tài)變量i(存值到內(nèi)存)

如果是單線程以上 8 行代碼是順序執(zhí)行(不會交錯)沒有問題:

在這里插入圖片描述

但多線程下這 8 行代碼可能交錯運行:

出現(xiàn)負數(shù)的情況:

在這里插入圖片描述

出現(xiàn)正數(shù)的情況:

在這里插入圖片描述

一個自增運算符是一個復(fù)合操作,“內(nèi)存取值”“寄存器增加1”和“存值到內(nèi)存”這三個JVM指令本身是不可再分的,它們都具備原子性,是線程安全的,也叫原子操作。但是,兩個或者兩個以上的原子操作合在一起進行操作就不再具備原子性了。比如先讀后寫,就有可能在讀之后,其實這個變量被修改了,出現(xiàn)讀和寫數(shù)據(jù)不一致的情況。

因為這4個操作之間是可以發(fā)生線程切換的,或者說是可以被其他線程中斷的。所以,++操作不是原子操作,在并行場景會發(fā)生原子性問題。

2. 可見性問題

一個線程對共享變量的修改,另一個線程能夠立刻可見,我們稱該共享變量具備內(nèi)存可見性。

談到內(nèi)存可見性,要先引出Java內(nèi)存模型的概念。JMM規(guī)定,將所有的變量都存放在公共主存中,當(dāng)線程使用變量時會把主存中的變量復(fù)制到自己的工作內(nèi)存(私有內(nèi)存)中,線程對變量的讀寫操作,是自己工作內(nèi)存中的變量副本。

如果兩個線程同時操作一個共享變量,就可能發(fā)生可見性問題:

(1) 主存中有變量sum,初始值sum=0;

(2) 線程A計劃將sum加1,先將sum=0復(fù)制到自己的私有內(nèi)存中,然后更新sum的值,線程A操作完成之后其私有內(nèi)存中sum=1,然而線程A將更新后的sum值回刷到主存的時間是不固定的;

(3) 在線程A沒有回刷sum到主存前,剛好線程B同樣從主存中讀取sum,此時值為0,和線程A進行同樣的操作,最后期盼的sum=2目標(biāo)沒有達成,最終sum=1;

線程A和線程B并發(fā)操作sum發(fā)生內(nèi)存可見性問題:

在這里插入圖片描述

要想解決多線程的內(nèi)存可見性問題,所有線程都必須將共享變量刷新到主存,一種簡單的方案是:使用Java提供的關(guān)鍵字volatile修飾共享變量。

為什么Java局部變量、方法參數(shù)不存在內(nèi)存可見性問題?

在Java中,所有的局部變量、方法定義參數(shù)都不會在線程之間共享,所以也就不會有內(nèi)存可見性問題。所有的Object實例、Class實例和數(shù)組元素都存儲在JVM堆內(nèi)存中,堆內(nèi)存在線程之間共享,所以存在可見性問題。

3. 有序性問題

所謂程序的有序性,是指程序按照代碼的先后順序執(zhí)行。如果程序執(zhí)行的順序與代碼的先后順序不同,并導(dǎo)致了錯誤的結(jié)果,即發(fā)生了有序性問題。

@Slf4j
public class Test3 {
    private static volatile int x=0,y=0;
    private static int a=0,b=0;

    public static void main(String[] args) throws InterruptedException {
        for(int i=0;;i++){
            a=0;
            b=0;
            x=0;
            y=0;
            Thread t1 = new Thread(() -> {
                a = 1;
                x = b;
            });

            Thread t2 = new Thread(() -> {
                b = 1;
                y = a;
            });
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            // 假如t1線程先執(zhí)行,t2線程后執(zhí)行,則結(jié)果為a=1,x=0,b=1,y=1  (0,1)
            // 假如t2線程先執(zhí)行,t1線程后執(zhí)行,則結(jié)果為b=1,y=0,a=1,x=1  (1,0)
            // 假如t1線程和t2線程的指令是同時或交替執(zhí)行的,則結(jié)果為a=1,b=1,x=1,y=1 (1,1)
            // 但是不可能出現(xiàn)(0,0)
            if(x==0 && y==0){
                log.debug("x:{}, y:{}",x,y);
            }
        }
    }
}

由于并發(fā)執(zhí)行的無序性,賦值之后x、y的值可能為(1,0)、(0,1)或(1,1)。為什么呢?因為線程t1可能在線程t2開始之前就執(zhí)行完了,也可能線程t2在線程t1開始之前就執(zhí)行完了,甚至有可能二者的指令是同時或交替執(zhí)行的。

然而,執(zhí)行以上代碼時,出乎意料的事情發(fā)生了:這段代碼的執(zhí)行結(jié)果也可能是(0,0),部分結(jié)果如下:

19:37:32.113 [main] DEBUG com.example.test.Test3 - x:0, y:0
19:37:33.041 [main] DEBUG com.example.test.Test3 - x:0, y:0
19:37:34.501 [main] DEBUG com.example.test.Test3 - x:0, y:0
19:37:41.825 [main] DEBUG com.example.test.Test3 - x:0, y:0

于以上程序來說,(0,0)結(jié)果是錯誤的,意味著已經(jīng)發(fā)生了并發(fā)的有序性問題。為什么會出現(xiàn)(0,0)結(jié)果呢?可能在程序的執(zhí)行過程中發(fā)生了指令重排序。對于線程t1來說,可能a=1和x=b這兩個語句的賦值操作順序被顛倒了,對于線程t2來說,可能b=1和y=a這兩個語句的賦值操作順序被顛倒了,從而出現(xiàn)了(x,y)值為(0,0)的錯誤結(jié)果。

什么是指令重排序?

一般來說,CPU為了提高程序運行效率,可能會對輸入代碼進行優(yōu)化,它不保證程序中各個語句的執(zhí)行順序同代碼中的先后順序一致,但是它會保證程序最終的執(zhí)行結(jié)果和代碼順序執(zhí)行的結(jié)果是一致的。

重排序也是單核時代非常優(yōu)秀的優(yōu)化手段,有足夠多的措施保證其在單核下的正確性。在多核時代,如果工作線程之間不共享數(shù)據(jù)或僅共享不可變數(shù)據(jù),重排序也是性能優(yōu)化的利器。然而,如果工作線程之間共享了可變數(shù)據(jù),由于兩種重排序的結(jié)果都不是固定的,因此會導(dǎo)致工作線程似乎表現(xiàn)出了隨機行為。指令重排序不會影響單個線程的執(zhí)行,但是會影響多個線程并發(fā)執(zhí)行的正確性。

事實上,輸出了亂序的結(jié)果,并不代表一定發(fā)生了指令重排序,內(nèi)存可見性問題也會導(dǎo)致這樣的輸出。但是,指令重排序也是導(dǎo)致亂序的原因之一。

總之,要想并發(fā)程序正確地執(zhí)行,必須要保證原子性、可見性以及有序性。只要有一個沒有得到保證,就有可能會導(dǎo)致程序運行不正確。

總結(jié)

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!     

相關(guān)文章

  • 詳解Java spring 中類注冊六種方式

    詳解Java spring 中類注冊六種方式

    在Spring中,將類注冊為容器管理的Bean有多種方式,涵蓋注解驅(qū)動、配置類、XML、動態(tài)注冊等場景,本文給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧
    2025-09-09
  • Java替換字符串replace和replaceAll方法舉例詳解

    Java替換字符串replace和replaceAll方法舉例詳解

    這篇文章主要介紹了Java中替換字符串的幾種方法,包括String類的replace()、replaceAll()、replaceFirst()方法,以及StringBuilder和StringBuffer類的replace()方法,還提到了一些第三方庫,如Hutool,它們提供了更豐富的字符串處理功能,需要的朋友可以參考下
    2025-02-02
  • Java中的接口多繼承機制

    Java中的接口多繼承機制

    大家好,本篇文章主要講的是Java中的接口多繼承機制,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-02-02
  • java隊列之queue用法實例分析

    java隊列之queue用法實例分析

    這篇文章主要介紹了java隊列之queue用法實例分析,Queue 隊列就是一個先入先出(FIFO)的數(shù)據(jù)結(jié)構(gòu),Queue接口繼承Collection接口。感興趣的可以了解一下
    2020-07-07
  • Java Socket編程實現(xiàn)群聊實踐案例

    Java Socket編程實現(xiàn)群聊實踐案例

    本文介紹了如何實現(xiàn)特定客戶端與特定客戶端之間的私聊和群聊功能,通過服務(wù)器端的多線程處理和客戶端的Socket連接,實現(xiàn)了消息的路由和轉(zhuǎn)發(fā),本文給大家介紹Java Socket編程實現(xiàn)群聊功能,感興趣的朋友跟隨小編一起看看吧
    2025-12-12
  • 后端報TypeError:Cannot?read?properties?of?null?(reading?‘xxx‘)的錯誤解決

    后端報TypeError:Cannot?read?properties?of?null?(reading?‘

    這篇文章主要給大家介紹了關(guān)于后端報TypeError:Cannot?read?properties?of?null?(reading?‘xxx‘)錯誤的解決辦法,這個錯誤是開發(fā)中常見的錯誤之一,需要的朋友可以參考下
    2023-05-05
  • SpringBoot集成內(nèi)存數(shù)據(jù)庫H2的實踐

    SpringBoot集成內(nèi)存數(shù)據(jù)庫H2的實踐

    h2是內(nèi)存數(shù)據(jù)庫,查詢高效,可以在開發(fā)初期使用它。本文主要介紹了SpringBoot集成內(nèi)存數(shù)據(jù)庫H2的實踐,具有一定的參考價值,感興趣的可以了解一下
    2021-09-09
  • 基于Mybatis-Plus攔截器實現(xiàn)MySQL數(shù)據(jù)加解密的示例代碼

    基于Mybatis-Plus攔截器實現(xiàn)MySQL數(shù)據(jù)加解密的示例代碼

    用戶的一些敏感數(shù)據(jù),例如手機號、郵箱、身份證等信息,在數(shù)據(jù)庫以明文存儲時會存在數(shù)據(jù)泄露的風(fēng)險,因此需要進行加密,解密等功能,接下來本文就給大家介紹基于Mybatis-Plus攔截器實現(xiàn)MySQL數(shù)據(jù)加解密,需要的朋友可以參考下
    2023-07-07
  • Java調(diào)用Windows的DOS命令的方法

    Java調(diào)用Windows的DOS命令的方法

    這篇文章主要介紹了Java調(diào)用Windows的DOS命令的方法,其主要內(nèi)容思路是實現(xiàn)調(diào)用Windows的ipconfig命令,然后將輸出的信息通過IO流輸出到控制臺。有需要的小伙伴參考下
    2015-02-02
  • Java開發(fā)Spark應(yīng)用程序自定義PipeLineStage詳解

    Java開發(fā)Spark應(yīng)用程序自定義PipeLineStage詳解

    這篇文章主要為大家介紹了Java開發(fā)Spark應(yīng)用程序自定義PipeLineStage詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02

最新評論

SHOW| 图们市| 绿春县| 区。| 涞水县| 泗水县| 通许县| 酒泉市| 上杭县| 吉安县| 伊宁县| 绥中县| 苏尼特右旗| 平邑县| 佛学| 阜城县| 铁岭县| 沾益县| 潞城市| 鸡泽县| 杂多县| 车致| 庐江县| 万全县| 临清市| 烟台市| 维西| 荣昌县| 邳州市| 达拉特旗| 正定县| 喀什市| 新乡县| 长葛市| 大姚县| 阜新| 海宁市| 盖州市| 桂东县| 泰宁县| 扎囊县|