" />

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

Java多線程之線程安全問(wèn)題詳解

 更新時(shí)間:2022年03月02日 16:56:35   作者:小小茶花女  
這篇文章主要為大家詳細(xì)介紹了Java多線程之線程安全問(wèn)題,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助

面試題:

  • 什么是線程安全和線程不安全?
  • 自增運(yùn)算是不是線程安全的?如何保證多線程下 i++ 結(jié)果正確?

1. 什么是線程安全和線程不安全?

什么是線程安全呢?當(dāng)多個(gè)線程并發(fā)訪問(wèn)某個(gè)Java對(duì)象時(shí),無(wú)論系統(tǒng)如何調(diào)度這些線程,也無(wú)論這些線程將如何交替操作,這個(gè)對(duì)象都能表現(xiàn)出一致的、正確的行為,那么對(duì)這個(gè)對(duì)象的操作是線程安全的。

如果這個(gè)對(duì)象表現(xiàn)出不一致的、錯(cuò)誤的行為,那么對(duì)這個(gè)對(duì)象的操作不是線程安全的,發(fā)生了線程的安全問(wèn)題。

2. 自增運(yùn)算為什么不是線程安全的?

線程安全實(shí)驗(yàn):兩個(gè)線程對(duì)初始值為 0 的靜態(tài)變量一個(gè)做自增,一個(gè)做自減,各做 5000 次,結(jié)果是 0 嗎?具體的代碼如下

public class ThreadDemo {
    private static int i = 0;
    public static void main(String[] args) throws InterruptedException {
        // 線程1對(duì)變量i做5000次自增運(yùn)算
         Thread t1 = new Thread(()->{
             for(int j=0;j<5000;j++){
                 i++;
             }
         });
         Thread t2 = new Thread(()->{
             for(int j=0;j<5000;j++){
                 i--;
             }
         });
         t1.start();
         t2.start();
         // 主線程等待t1線程和t2線程執(zhí)行結(jié)束再繼續(xù)執(zhí)行
         t1.join();
         t2.join();
        System.out.println(i);// 581 / -1830 / 0
    }
}

以上的結(jié)果可能是正數(shù)、負(fù)數(shù)、零。為什么呢?因?yàn)?Java 中對(duì)靜態(tài)變量的自增,自減并不是原子操作,要徹底理解,必須從字節(jié)碼來(lái)進(jìn)行分析。

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

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

而對(duì)應(yīng) i-- 也是類似:

getstatic i  // 獲取靜態(tài)變量i的值
iconst_1     // 準(zhǔn)備常量1
isub         // 自減
putstatic i  // 將修改后的值存入靜態(tài)變量

而 Java 的內(nèi)存模型如下,完成靜態(tài)變量的自增,自減需要在主存和工作內(nèi)存中進(jìn)行數(shù)據(jù)交換:

在這里插入圖片描述

如果是單線程以上 8 行代碼是順序執(zhí)行(不會(huì)交錯(cuò))沒(méi)有問(wèn)題:

在這里插入圖片描述

但多線程下這 8 行代碼可能交錯(cuò)運(yùn)行:

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

在這里插入圖片描述

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

在這里插入圖片描述

因此,一個(gè)自增運(yùn)算符是一個(gè)復(fù)合操作,至少包括三個(gè)JVM指令:“內(nèi)存取值”“寄存器增加1”和“存值到內(nèi)存”。這三個(gè)指令在JVM內(nèi)部是獨(dú)立進(jìn)行的,中間完全可能會(huì)出現(xiàn)多個(gè)線程并發(fā)進(jìn)行。“內(nèi)存取值”“寄存器增加1”和“存值到內(nèi)存”這三個(gè)JVM指令本身是不可再分的,它們都具備原子性,是線程安全的,也叫原子操作。但是,兩個(gè)或者兩個(gè)以上的原子操作合在一起進(jìn)行操作就不再具備原子性了。比如先讀后寫(xiě),就有可能在讀之后,其實(shí)這個(gè)變量被修改了,出現(xiàn)讀和寫(xiě)數(shù)據(jù)不一致的情況。

3. 臨界區(qū)資源和競(jìng)態(tài)條件

在多個(gè)線程操作相同資源(如變量、數(shù)組或者對(duì)象)時(shí)就可能出現(xiàn)線程安全問(wèn)題。一般來(lái)說(shuō),只在多個(gè)線程對(duì)這個(gè)資源進(jìn)行寫(xiě)操作的時(shí)候才會(huì)出現(xiàn)問(wèn)題,如果是簡(jiǎn)單的讀操作,不改變資源的話,顯然是不會(huì)出現(xiàn)問(wèn)題的。

臨界區(qū)資源表示一種可以被多個(gè)線程使用的公共資源或共享數(shù)據(jù),但是每一次只能有一個(gè)線程使用它。一旦臨界區(qū)資源被占用,想使用該資源的其他線程則必須等待。在并發(fā)情況下,臨界區(qū)資源是受保護(hù)的對(duì)象。

臨界區(qū)代碼段是每個(gè)線程中訪問(wèn)臨界資源的那段代碼,多個(gè)線程必須互斥地對(duì)臨界區(qū)資源進(jìn)行訪問(wèn)。線程進(jìn)入臨界區(qū)代碼段之前,必須在進(jìn)入?yún)^(qū)申請(qǐng)資源,申請(qǐng)成功之后執(zhí)行臨界區(qū)代碼段,執(zhí)行完成之后釋放資源。臨界區(qū)代碼段的進(jìn)入和退出如圖所示:

在這里插入圖片描述

競(jìng)態(tài)條件可能是由于在訪問(wèn)臨界區(qū)代碼段時(shí)沒(méi)有互斥地訪問(wèn)而導(dǎo)致的特殊情況。如果多個(gè)線程在臨界區(qū)代碼段的并發(fā)執(zhí)行結(jié)果可能因?yàn)榇a的執(zhí)行順序不同而不同,我們就說(shuō)這時(shí)在臨界區(qū)出現(xiàn)了競(jìng)態(tài)條件問(wèn)題。

比如下面代碼中的臨界區(qū)資源和臨界區(qū)代碼段:

public class SafeDemo {
    // 臨界區(qū)資源
    private static int i = 0;
    // 臨界區(qū)代碼段
    public void selfIncrement(){
        for(int j=0;j<5000;j++){
            i++;
        }
    }
    // 臨界區(qū)代碼段
    public void selfDecrement(){
        for(int j=0;j<5000;j++){
            i--;
        }
    }
	// 這個(gè)不是臨界區(qū)代碼,因?yàn)殡m然使用了共享資源,但是這個(gè)方法并沒(méi)有被多個(gè)線程同時(shí)訪問(wèn)
    public int getI(){
        return i;
    }
}
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        SafeDemo safeDemo = new SafeDemo();
        Thread t1 = new Thread(()->{
            safeDemo.selfIncrement();
        });
        Thread t2 = new Thread(()->{
            safeDemo.selfDecrement();
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(safeDemo.getI());
    }
}

當(dāng)多個(gè)線程訪問(wèn)臨界區(qū)的selfIncrement()方法時(shí),就會(huì)出現(xiàn)競(jìng)態(tài)條件的問(wèn)題。更標(biāo)準(zhǔn)地說(shuō),當(dāng)兩個(gè)或多個(gè)線程競(jìng)爭(zhēng)同一個(gè)資源時(shí),對(duì)資源的訪問(wèn)順序就變得非常關(guān)鍵。為了避免競(jìng)態(tài)條件的問(wèn)題,我們必須保證臨界區(qū)代碼段操作具備排他性。這就意味著當(dāng)一個(gè)線程進(jìn)入臨界區(qū)代碼段執(zhí)行時(shí),其他線程不能進(jìn)入臨界區(qū)代碼段執(zhí)行。

總結(jié):

(1) 一個(gè)程序運(yùn)行多個(gè)線程本身是沒(méi)有問(wèn)題的,問(wèn)題出在多個(gè)線程訪問(wèn)共享資源,多個(gè)線程讀共享資源其實(shí)也沒(méi)有問(wèn)題,而在多個(gè)線程對(duì)共享資源讀寫(xiě)操作時(shí)發(fā)生指令交錯(cuò),就會(huì)出現(xiàn)問(wèn)題 ;

(2) 一段代碼塊內(nèi)如果存在對(duì)共享資源的多線程讀寫(xiě)操作,稱這段代碼塊為臨界區(qū)代碼塊;

(3) 多個(gè)線程在臨界區(qū)內(nèi)執(zhí)行,由于代碼的執(zhí)行序列不同而導(dǎo)致結(jié)果無(wú)法預(yù)測(cè),稱之為發(fā)生了競(jìng)態(tài)條件;

在Java中,可以使用synchronized關(guān)鍵字,使用Lock顯式鎖實(shí)例,或者使用原子變量(AtomicVariables)對(duì)臨界區(qū)代碼段進(jìn)行排他性保護(hù)。

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

相關(guān)文章

  • 搞懂Java線程池

    搞懂Java線程池

    這篇文章主要介紹了Java線程池,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 微信游戲打飛機(jī)游戲制作(java模擬微信打飛機(jī)游戲)

    微信游戲打飛機(jī)游戲制作(java模擬微信打飛機(jī)游戲)

    java模擬微信打飛機(jī)游戲,大家參考使用吧
    2013-12-12
  • Java觀察者模式例子

    Java觀察者模式例子

    這篇文章主要介紹了Java觀察者模式例子的相關(guān)資料,需要的朋友可以參考下
    2015-12-12
  • Java遞歸讀取文件例子_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java遞歸讀取文件例子_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    本文通過(guò)一段示例代碼給大家介紹了java遞歸讀取文件的方法,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧
    2017-05-05
  • java實(shí)現(xiàn)ArrayList根據(jù)存儲(chǔ)對(duì)象排序功能示例

    java實(shí)現(xiàn)ArrayList根據(jù)存儲(chǔ)對(duì)象排序功能示例

    這篇文章主要介紹了java實(shí)現(xiàn)ArrayList根據(jù)存儲(chǔ)對(duì)象排序功能,結(jié)合實(shí)例形式分析了java針對(duì)ArrayList的相關(guān)運(yùn)算、排序操作技巧,需要的朋友可以參考下
    2018-01-01
  • 如何搭建一個(gè)完整的Java開(kāi)發(fā)環(huán)境

    如何搭建一個(gè)完整的Java開(kāi)發(fā)環(huán)境

    這篇文章主要教大家如何搭建一個(gè)完整的Java開(kāi)發(fā)環(huán)境,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • Springboot校驗(yàn)工具類詳細(xì)代碼示例

    Springboot校驗(yàn)工具類詳細(xì)代碼示例

    這篇文章主要給大家介紹了關(guān)于Springboot校驗(yàn)工具類的相關(guān)資料,工具類里面主要是封裝了一些常用字段驗(yàn)證方法,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-02-02
  • 關(guān)于HashMap源碼解讀

    關(guān)于HashMap源碼解讀

    HashMap是基于哈希表的Map接口實(shí)現(xiàn),主要用于存儲(chǔ)鍵值對(duì),它通過(guò)數(shù)組、鏈表和紅黑樹(shù)來(lái)實(shí)現(xiàn),解決了哈希沖突問(wèn)題,Java?8中,HashMap對(duì)數(shù)據(jù)結(jié)構(gòu)進(jìn)行了優(yōu)化,引入紅黑樹(shù)來(lái)提高查找效率,此外,HashMap是非線程安全的,適用于單線程環(huán)境
    2024-09-09
  • SpringBoot整合微信小程序支付V3(支付退款)

    SpringBoot整合微信小程序支付V3(支付退款)

    小程序支付在很多項(xiàng)目都會(huì)使用,本文主要介紹了SpringBoot整合微信小程序支付V3,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-09-09
  • Java位運(yùn)算知識(shí)點(diǎn)詳解

    Java位運(yùn)算知識(shí)點(diǎn)詳解

    這篇文章給大家分享了關(guān)于Java位運(yùn)算的相關(guān)知識(shí)點(diǎn)內(nèi)容,有興趣的朋友們可以學(xué)習(xí)參考下。
    2018-09-09

最新評(píng)論

丹巴县| 长顺县| 隆尧县| 抚松县| 青州市| 黄陵县| 梅河口市| 会同县| 淅川县| 晴隆县| 罗江县| 修武县| 清徐县| 米泉市| 察哈| 镇宁| 固始县| 广平县| 汝城县| 荃湾区| 北流市| 区。| 永泰县| 察哈| 罗田县| 长顺县| 昌黎县| 京山县| 达州市| 丘北县| 新竹市| 遵化市| 秦皇岛市| 鹰潭市| 开封市| 应用必备| 房山区| 和田县| 皮山县| 泌阳县| 桐乡市|