Java解決線程的不安全問題之volatile關鍵字詳解
1. 造成線程不安全的代碼
有一代碼,要求兩個線程運行。
并自定義一個標志位 flag,當線程2(thread2)修改標志位后,線程1(thread1)結束執(zhí)行。
如下代碼所示:
public class TestDemo3 {
public static int flag = 0;//自定義一個標志位
public static void main(String[] args) {
Thread thread1 = new Thread(()-> {
while (flag == 0) {
//空
}
System.out.println("thread1線程結束");
});//線程1
Thread thread2 = new Thread(()-> {
Scanner scanner = new Scanner(System.in);
System.out.println("請輸入一個整數(shù):");
flag = scanner.nextInt();
});//線程2
thread1.start();//啟動線程1
thread2.start();//啟動線程2
}
}運行后打印:

預期效果為:thread1 中的 flag==0 作為條件進入 while 循序,thread2 中通過 scanner 輸入一個非 0 的值,從而使得 thread1 線程結束。
實際效果:thread2 中輸入非 0 數(shù)后,光標處于閃爍狀態(tài)代表循環(huán)未結束。
造成程序沒有達到如期效果的原因是內存的不可見性導致 while 條件判斷始終發(fā)生錯誤。
因此,我們得使用 volatile 關鍵字來保證內存的可見性,使得 while 條件判斷能夠正常識別修改后的標志位 flag。
2. volatile能保證內存可見性
可見性指一個線程對共享變量值的修改,能夠及時地被其他線程看到。
而 volatile 關鍵字就保證內存的可見性。
在上述代碼中標志位 flag 未使用 volatile 修飾導致 while 循環(huán)不能正確判斷,其原因如下:
flag == 0這個判斷,會實現(xiàn)兩條操作:
- 第一條,load 從內存讀取數(shù)據(jù)到 cpu的 寄存器。
- 第二條,cmp 比較寄存器中的值是否為0,是則返回 true 否則返回 false。
但是,編譯器有一個特性:優(yōu)化。優(yōu)化什么呢?
由于進行大量數(shù)據(jù)操作時 load 的開銷很大,編譯器就做出了一個優(yōu)化,就是無論數(shù)據(jù)大或小 load 操作只會執(zhí)行一次。
因此,flag == 0 這個條件第一作為 load 加載到了寄存器中,后序無論對 flag 進行怎樣的修改 cmp 比較的時候始終為 true 了。

這就是多線程運行時,編譯器對于代碼進行優(yōu)化操作的內存不可見性。也就是內存看不到實際的情況。
因此,我們只需要在 flag 前面加上 volatile 關鍵字使得編譯器不對 flag 進行優(yōu)化,這樣就能達到效果。如下代碼所示:
public class TestDemo3 {
volatile public static int flag = 0;//volatile修飾自定義標志位
public static void main(String[] args) {
Thread thread1 = new Thread(()-> {
while (flag == 0) {
//空
}
System.out.println("thread1線程結束");
});//線程1
Thread thread2 = new Thread(()-> {
Scanner scanner = new Scanner(System.in);
System.out.println("請輸入一個整數(shù):");
flag = scanner.nextInt();
});//線程2
thread1.start();//啟動線程1
thread2.start();//啟動線程2
}
}運行后打?。?/p>

通過上述代碼及打印結果,可以看到達到了預期效果。因此,被 volatile 修飾的變量能夠保證每次從內存中重新讀取數(shù)據(jù)。
解釋內存可見性:
thread1頻繁讀取主內存,效率比較第,就被優(yōu)化成直接讀直接的工作內存
thread2修改了主內存的結果,由于thread1沒有讀主內存,導致修改不能被識別
上述的工作內存理解為CPU寄存器,主內存理解為內存。
3. synchronized與volatile的區(qū)別
3.1 synchronized能保證原子性
以下代碼的需求為:兩個線程分別計算10000 次,使得 count 總數(shù)達到 20000:
//創(chuàng)建一個自定義類
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();//實例化這個類
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();//啟動線程thread1
thread2.start();//啟動線程thread2
thread1.join();//等待線程thread1結束
thread2.join();//等待線程thread2結束
System.out.println(myThread.getCount());//獲取count值
}
}運行后打印:

3.2 volatile不能保證原子性
當我們把上述代碼中的 run 方法去掉 synchronized 的關鍵字,再給 count 變量加上 volatile 關鍵字。

//創(chuàng)建一個自定義類
class myThread {
volatile int count = 0;
public void run() {
count++;
}
public int getCount() {
return count;
}
}運行后打印:

到此這篇關于Java解決線程的不安全問題之volatile關鍵字詳解的文章就介紹到這了,更多相關Java的volatile關鍵字內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java零基礎教程之do-while循環(huán)語句實例
Java中的do-while循環(huán)是一種后測試循環(huán)語句,它類似于while循環(huán),但它先執(zhí)行循環(huán)體中的代碼,然后再進行條件判斷,無論條件是否滿足,至少會執(zhí)行一次循環(huán)體中的代碼,這篇文章主要給大家介紹了關于Java零基礎教程之do-while循環(huán)語句的相關資料,需要的朋友可以參考下2024-09-09
SpringBoot配置shiro安全框架的實現(xiàn)
這篇文章主要介紹了SpringBoot配置shiro安全框架的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-04-04
Jpa 實現(xiàn)自動更新表中的創(chuàng)建日期和修改時間
這篇文章主要介紹了Jpa 實現(xiàn)自動更新表中的創(chuàng)建日期和修改時間,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-01-01
SpringCloud通過Nacos實現(xiàn)注冊中心與遠程服務調用詳解流程
如果不滿足eureka注冊中心,那么本文記錄的Nacos是不二之選。本文主要記錄Springboot基于Nacos實現(xiàn)注冊中心以及遠程服務調用2022-07-07
idea中使用maven?archetype新建項目時卡住問題解決方案
這篇文章主要介紹了idea中使用maven?archetype新建項目時卡住,解決本問題的方法,就是在maven的runner加上參數(shù)-DarchetypeCatalog=local就可以了,不需要下載xml文件再放到指定目錄,需要的朋友可以參考下2023-08-08
SpringBoot請求處理之常用參數(shù)注解介紹與源碼分析
SpringBoot是一種整合Spring技術棧的方式(或者說是框架),同時也是簡化Spring的一種快速開發(fā)的腳手架,本篇讓我們一起學習請求處理、常用注解和方法參數(shù)的小技巧2022-10-10
Java如何獲取一個隨機數(shù) Java猜數(shù)字小游戲
這篇文章主要為大家詳細介紹了Java如何獲取一個隨機數(shù),類似猜數(shù)字小游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-03-03

