欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java解決線程的不安全問(wèn)題之volatile關(guān)鍵字詳解

 更新時(shí)間:2023年08月26日 09:07:23   作者:一只愛(ài)打拳的程序猿  
這篇文章主要介紹了Java解決線程的不安全問(wèn)題之volatile關(guān)鍵字詳解,可見(jiàn)性指一個(gè)線程對(duì)共享變量值的修改,能夠及時(shí)地被其他線程看到,而 volatile 關(guān)鍵字就保證內(nèi)存的可見(jiàn)性,需要的朋友可以參考下

1. 造成線程不安全的代碼

有一代碼,要求兩個(gè)線程運(yùn)行。

并自定義一個(gè)標(biāo)志位 flag,當(dāng)線程2(thread2)修改標(biāo)志位后,線程1(thread1)結(jié)束執(zhí)行。

如下代碼所示:

public class TestDemo3 {
    public static int flag = 0;//自定義一個(gè)標(biāo)志位
    public static void main(String[] args) {
        Thread thread1 = new Thread(()-> {
            while (flag == 0) {
                //空
            }
            System.out.println("thread1線程結(jié)束");
        });//線程1
        Thread thread2 = new Thread(()-> {
            Scanner scanner = new Scanner(System.in);
            System.out.println("請(qǐng)輸入一個(gè)整數(shù):");
            flag = scanner.nextInt();
        });//線程2
        thread1.start();//啟動(dòng)線程1
        thread2.start();//啟動(dòng)線程2
    }
}

運(yùn)行后打印:

預(yù)期效果為:thread1 中的 flag==0 作為條件進(jìn)入 while 循序,thread2 中通過(guò) scanner 輸入一個(gè)非 0 的值,從而使得 thread1 線程結(jié)束。

實(shí)際效果:thread2 中輸入非 0 數(shù)后,光標(biāo)處于閃爍狀態(tài)代表循環(huán)未結(jié)束。

造成程序沒(méi)有達(dá)到如期效果的原因是內(nèi)存的不可見(jiàn)性導(dǎo)致 while 條件判斷始終發(fā)生錯(cuò)誤。

因此,我們得使用 volatile 關(guān)鍵字來(lái)保證內(nèi)存的可見(jiàn)性,使得 while 條件判斷能夠正常識(shí)別修改后的標(biāo)志位 flag。

2. volatile能保證內(nèi)存可見(jiàn)性

可見(jiàn)性指一個(gè)線程對(duì)共享變量值的修改,能夠及時(shí)地被其他線程看到。

而 volatile 關(guān)鍵字就保證內(nèi)存的可見(jiàn)性。

在上述代碼中標(biāo)志位 flag 未使用 volatile 修飾導(dǎo)致 while 循環(huán)不能正確判斷,其原因如下:

flag == 0這個(gè)判斷,會(huì)實(shí)現(xiàn)兩條操作:

  • 第一條,load 從內(nèi)存讀取數(shù)據(jù)到 cpu的 寄存器。
  • 第二條,cmp 比較寄存器中的值是否為0,是則返回 true 否則返回 false。

但是,編譯器有一個(gè)特性:優(yōu)化。優(yōu)化什么呢?

由于進(jìn)行大量數(shù)據(jù)操作時(shí) load 的開(kāi)銷(xiāo)很大,編譯器就做出了一個(gè)優(yōu)化,就是無(wú)論數(shù)據(jù)大或小 load 操作只會(huì)執(zhí)行一次。

因此,flag == 0 這個(gè)條件第一作為 load 加載到了寄存器中,后序無(wú)論對(duì) flag 進(jìn)行怎樣的修改 cmp 比較的時(shí)候始終為 true 了。

這就是多線程運(yùn)行時(shí),編譯器對(duì)于代碼進(jìn)行優(yōu)化操作的內(nèi)存不可見(jiàn)性。也就是內(nèi)存看不到實(shí)際的情況。

因此,我們只需要在 flag 前面加上 volatile 關(guān)鍵字使得編譯器不對(duì) flag 進(jìn)行優(yōu)化,這樣就能達(dá)到效果。如下代碼所示:

public class TestDemo3 {
    volatile public static int flag = 0;//volatile修飾自定義標(biāo)志位
    public static void main(String[] args) {
        Thread thread1 = new Thread(()-> {
            while (flag == 0) {
                //空
            }
            System.out.println("thread1線程結(jié)束");
        });//線程1
        Thread thread2 = new Thread(()-> {
            Scanner scanner = new Scanner(System.in);
            System.out.println("請(qǐng)輸入一個(gè)整數(shù):");
            flag = scanner.nextInt();
        });//線程2
        thread1.start();//啟動(dòng)線程1
        thread2.start();//啟動(dòng)線程2
    }
}

運(yùn)行后打?。?/p>

通過(guò)上述代碼及打印結(jié)果,可以看到達(dá)到了預(yù)期效果。因此,被 volatile 修飾的變量能夠保證每次從內(nèi)存中重新讀取數(shù)據(jù)。

解釋內(nèi)存可見(jiàn)性:

thread1頻繁讀取主內(nèi)存,效率比較第,就被優(yōu)化成直接讀直接的工作內(nèi)存

thread2修改了主內(nèi)存的結(jié)果,由于thread1沒(méi)有讀主內(nèi)存,導(dǎo)致修改不能被識(shí)別 

上述的工作內(nèi)存理解為CPU寄存器,主內(nèi)存理解為內(nèi)存。 

3. synchronized與volatile的區(qū)別

3.1 synchronized能保證原子性

以下代碼的需求為:兩個(gè)線程分別計(jì)算10000 次,使得 count 總數(shù)達(dá)到 20000:

//創(chuàng)建一個(gè)自定義類(lèi)
class myThread {
    int count = 0;
    public void run() {
        synchronized (this){
            count++;
        }
    }
    public int getCount() {
        return count;
    }
}
public class TreadDemo1 {
    public static void main(String[] args) throws InterruptedException {
        myThread myThread = new myThread();//實(shí)例化這個(gè)類(lèi)
        Thread thread1 = new Thread(()-> {
            for (int i = 0; i < 10000; i++) {
                myThread.run();
            }
        });
        Thread thread2 = new Thread(()-> {
            for (int i = 0; i < 10000; i++) {
                myThread.run();
            }
        });
        thread1.start();//啟動(dòng)線程thread1
        thread2.start();//啟動(dòng)線程thread2
        thread1.join();//等待線程thread1結(jié)束
        thread2.join();//等待線程thread2結(jié)束
        System.out.println(myThread.getCount());//獲取count值
    }
}

運(yùn)行后打印:

3.2 volatile不能保證原子性

當(dāng)我們把上述代碼中的 run 方法去掉 synchronized 的關(guān)鍵字,再給 count 變量加上 volatile 關(guān)鍵字。

//創(chuàng)建一個(gè)自定義類(lèi)
class myThread {
    volatile int count = 0;
    public void run() {
       count++;
    }
    public int getCount() {
        return count;
    }
}

運(yùn)行后打印:

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

相關(guān)文章

  • spring boot 常見(jiàn)http請(qǐng)求url參數(shù)獲取方法

    spring boot 常見(jiàn)http請(qǐng)求url參數(shù)獲取方法

    這篇文章主要介紹了spring boot 常見(jiàn)http請(qǐng)求url參數(shù)獲取,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • Springboot中的自定義攔截器及原理詳解

    Springboot中的自定義攔截器及原理詳解

    這篇文章主要介紹了Springboot中的自定義攔截器及原理詳解,攔截器主要是用于在用戶請(qǐng)求控制中,對(duì)于請(qǐng)求識(shí)別,鑒權(quán),以及區(qū)分資源是否可以被目標(biāo)方法調(diào)用的安全機(jī)制,需要的朋友可以參考下
    2023-12-12
  • Java1.8中LocalDate方法使用總結(jié)

    Java1.8中LocalDate方法使用總結(jié)

    LocalDate是Java8中的一個(gè)日期類(lèi),用于表示年月日,它是不可變的,線程安全的,下面這篇文章主要給大家介紹了關(guān)于Java1.8中LocalDate方法使用的相關(guān)資料,需要的朋友可以參考下
    2024-03-03
  • Java基于對(duì)象流實(shí)現(xiàn)銀行系統(tǒng)

    Java基于對(duì)象流實(shí)現(xiàn)銀行系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了Java基于對(duì)象流實(shí)現(xiàn)銀行系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • 實(shí)戰(zhàn)分布式醫(yī)療掛號(hào)系統(tǒng)開(kāi)發(fā)醫(yī)院科室及排班的接口

    實(shí)戰(zhàn)分布式醫(yī)療掛號(hào)系統(tǒng)開(kāi)發(fā)醫(yī)院科室及排班的接口

    這篇文章主要為大家介紹了實(shí)戰(zhàn)分布式醫(yī)療掛號(hào)系統(tǒng)開(kāi)發(fā)醫(yī)院科室及排班的接口,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>
    2022-04-04
  • 淺析SpringBoot中的過(guò)濾器和攔截器

    淺析SpringBoot中的過(guò)濾器和攔截器

    過(guò)濾器和攔截器都是為了在請(qǐng)求到達(dá)目標(biāo)處理器(Servlet或Controller)之前或者之后插入自定義的處理邏輯,下面就跟隨小編來(lái)看看它們二者的區(qū)別和具體使用吧
    2024-03-03
  • 盤(pán)點(diǎn)Java中延時(shí)任務(wù)的多種實(shí)現(xiàn)方式

    盤(pán)點(diǎn)Java中延時(shí)任務(wù)的多種實(shí)現(xiàn)方式

    當(dāng)需要一個(gè)定時(shí)發(fā)布系統(tǒng)通告的功能,如何實(shí)現(xiàn)??當(dāng)支付超時(shí),訂單自動(dòng)取消,如何實(shí)現(xiàn)?其實(shí)這些問(wèn)題本質(zhì)都是延時(shí)任務(wù)的實(shí)現(xiàn),本文為大家盤(pán)點(diǎn)了多種常見(jiàn)的延時(shí)任務(wù)實(shí)現(xiàn)方法,希望對(duì)大家有所幫助
    2022-12-12
  • Java CPU性能分析工具代碼實(shí)例

    Java CPU性能分析工具代碼實(shí)例

    這篇文章主要介紹了Java CPU性能分析工具代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • 細(xì)數(shù)java for循環(huán)中的那些坑

    細(xì)數(shù)java for循環(huán)中的那些坑

    這篇文章主要介紹了Java for循環(huán)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-07-07
  • Java如何通過(guò)File類(lèi)方法刪除指定文件夾中的全部文件

    Java如何通過(guò)File類(lèi)方法刪除指定文件夾中的全部文件

    這篇文章主要給大家介紹了關(guān)于Java如何通過(guò)File類(lèi)方法刪除指定文件夾中的全部文件的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01

最新評(píng)論