目錄
?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-624108.html
1. volatile
1.1?volatile關(guān)鍵字的作用
1.1.1?變量可見(jiàn)性
1.1.2?禁止指令重排序
1.2 volatile可見(jiàn)性案例
1.3 volatile非原子性案例
1.4 volatile 禁止重排序
1.5 volatile 日常使用場(chǎng)景
送書(shū)活動(dòng)
?
1. volatile
在并發(fā)編程中,多線程操作共享的變量時(shí),可能會(huì)導(dǎo)致線程安全問(wèn)題,如數(shù)據(jù)競(jìng)爭(zhēng)、可見(jiàn)性問(wèn)題等。為了解決這些問(wèn)題,Java提供了JUC(java.util.concurrent)工具包,其中包含了很多用于處理并發(fā)編程的工具類(lèi)和接口。在JUC中,volatile
是一個(gè)關(guān)鍵字,它可以用于修飾變量,用來(lái)確保變量的可見(jiàn)性和禁止指令重排序,從而在一定程度上解決線程安全問(wèn)題。
1.1?volatile關(guān)鍵字的作用
1.1.1?變量可見(jiàn)性
在多線程環(huán)境下,如果一個(gè)線程修改了共享變量的值,其他線程可能由于線程間的數(shù)據(jù)不一致性而看不到該變量的最新值。這種問(wèn)題稱(chēng)為“變量不可見(jiàn)性”或“可見(jiàn)性問(wèn)題”。
volatile
關(guān)鍵字可以確保被修飾的變量對(duì)所有線程可見(jiàn)。當(dāng)一個(gè)線程修改了volatile
變量的值,其他線程立即能夠看到修改后的最新值,而不會(huì)使用緩存中的舊值。
1.1.2?禁止指令重排序
在JVM(Java虛擬機(jī))中,為了優(yōu)化性能,編譯器和處理器可能會(huì)對(duì)指令進(jìn)行重排序。在單線程環(huán)境下,這種重排序不會(huì)影響程序的執(zhí)行結(jié)果。然而,在多線程環(huán)境下,指令重排序可能會(huì)導(dǎo)致線程安全問(wèn)題。
volatile
關(guān)鍵字可以防止指令重排序,確保被修飾的變量按照代碼中的順序執(zhí)行。
1.2 volatile可見(jiàn)性案例
public class VolatileExample {
private static volatile boolean flag = false;
public static void main(String[] args) {
new Thread(() -> {
while (!flag) {
System.out.println("Waiting for the flag to be true...");
}
System.out.println("Flag is now true. Exiting the thread.");
}).start();
try {
Thread.sleep(1000); // 確保主線程在子線程之前執(zhí)行
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Setting the flag to true...");
flag = true;
}
}
在上面的示例中,我們創(chuàng)建了一個(gè)VolatileExample
類(lèi),并聲明了一個(gè)volatile
類(lèi)型的flag
變量。在主線程中,我們啟動(dòng)一個(gè)新的子線程,該子線程會(huì)不斷地檢查flag
變量的值,直到flag
變?yōu)?code>true時(shí),子線程退出。
在主線程中,我們將flag
變量設(shè)置為true
。由于flag
變量被聲明為volatile
類(lèi)型,子線程能夠及時(shí)看到flag
的最新值,從而退出循環(huán),輸出“Flag is now true. Exiting the thread.”。
這個(gè)示例演示了volatile
關(guān)鍵字的作用,確保了flag
變量的可見(jiàn)性。如果我們沒(méi)有使用volatile
關(guān)鍵字,子線程可能會(huì)一直循環(huán)下去,因?yàn)樗床坏街骶€程對(duì)flag
的修改。
?
1.3 volatile非原子性案例
public class Test {
public static void main(String[] args) throws InterruptedException {
VolatileAtomicityExample example = new VolatileAtomicityExample();
for(int i=1;i<=100;i++){
new Thread(()->{
for(int j=1;j<=1000;j++)
example.increment();
},String.valueOf(i)).start();
}
TimeUnit.SECONDS.sleep(2);
System.out.println(example.getCount());
}
}
class VolatileAtomicityExample {
volatile int count = 0;
public void increment() {
count++;
}
public int getCount() {
return count;
}
}
我們創(chuàng)建了一個(gè)VolatileAtomicityExample
類(lèi),其中的成員變量count
被聲明為volatile
類(lèi)型。然后,我們創(chuàng)建了100個(gè)線程,每個(gè)線程分別執(zhí)行1000次increment()
操作,對(duì)count
進(jìn)行自增。最后,我們?cè)谥骶€程中打印count
的最終值。以上示例中的輸出結(jié)果可能會(huì)因?yàn)檫\(yùn)行時(shí)的不確定性而有所不同。每次運(yùn)行時(shí)可能得到不同的結(jié)果,但通常結(jié)果都小于100000。為了解決這個(gè)問(wèn)題,我們需要使用synchronized
關(guān)鍵字或其他線程安全機(jī)制來(lái)確保increment()
方法的原子性。
這是什么原因呢?
?對(duì)于volatile變量具備可見(jiàn)性,JVM只是保證從主內(nèi)存加載到線程工作內(nèi)存的值是最新的,也僅是數(shù)據(jù)加載時(shí)是最新的。但是多線程環(huán)境下,"數(shù)據(jù)計(jì)算"和"數(shù)據(jù)賦值"操作可能多次出現(xiàn),若數(shù)據(jù)在加載之后,若主內(nèi)存volatile修飾變量發(fā)生修改之后,線程工作內(nèi)存中的操作將會(huì)作廢去讀主內(nèi)存最新值,操作出現(xiàn)寫(xiě)丟失問(wèn)題。即各線程私有內(nèi)存和主內(nèi)存公共內(nèi)存中變量不同步,進(jìn)而導(dǎo)致數(shù)據(jù)不一致。由此可見(jiàn)volatile解決的是變量讀時(shí)的可見(jiàn)性問(wèn)題,但無(wú)法保證原子性,對(duì)于多線程修改主內(nèi)存共享變量的場(chǎng)景必須使用加鎖同步。
1.4 volatile 禁止重排序
內(nèi)存屏障是一種硬件指令或者編譯器指令,用于控制內(nèi)存操作的順序,以確保多線程環(huán)境下的內(nèi)存可見(jiàn)性和正確的執(zhí)行順序。
內(nèi)存屏障分為兩種類(lèi)型:
-
內(nèi)存讀屏障(Load Barrier):它是一個(gè)特殊的硬件或者編譯器指令,用于保證在內(nèi)存讀取操作之前,所有的先行寫(xiě)操作都已經(jīng)完成,并且其結(jié)果對(duì)當(dāng)前線程可見(jiàn)。也就是說(shuō),讀屏障可以防止后續(xù)讀取指令重排序到讀屏障之前的位置。
-
內(nèi)存寫(xiě)屏障(Store Barrier):它是一個(gè)特殊的硬件或者編譯器指令,用于保證在內(nèi)存寫(xiě)入操作之前,所有的先行寫(xiě)操作和寫(xiě)屏障之前的寫(xiě)操作都已經(jīng)完成,并且其結(jié)果對(duì)其他線程可見(jiàn)。也就是說(shuō),寫(xiě)屏障可以防止前面的寫(xiě)入指令重排序到寫(xiě)屏障之后的位置。
volatile
關(guān)鍵字通過(guò)內(nèi)存屏障來(lái)保證變量的讀寫(xiě)操作不會(huì)被重排序。具體來(lái)說(shuō),對(duì)于volatile
變量的寫(xiě)操作,在寫(xiě)入變量之后會(huì)插入寫(xiě)屏障,這樣可以防止其他指令重排序到寫(xiě)屏障之前。類(lèi)似地,對(duì)于volatile
變量的讀操作,在讀取變量之前會(huì)插入讀屏障,這樣可以防止其他指令重排序到讀屏障之前。
通過(guò)這種方式,volatile
關(guān)鍵字確保了對(duì)變量的讀寫(xiě)操作具有一定的有序性,從而保證了多線程環(huán)境下的內(nèi)存可見(jiàn)性和正確的執(zhí)行順序。
1.5 volatile 日常使用場(chǎng)景
狀態(tài)標(biāo)志:當(dāng)一個(gè)線程修改了某個(gè)狀態(tài)標(biāo)志,其他線程需要立即看到最新的狀態(tài)。這時(shí)可以使用volatile
關(guān)鍵字修飾狀態(tài)標(biāo)志,保證其在多線程之間的可見(jiàn)性。例如:
public class Task implements Runnable {
private volatile boolean isRunning = true;
@Override
public void run() {
while (isRunning) {
// 執(zhí)行任務(wù)邏輯
}
}
public void stop() {
isRunning = false;
}
}
雙重檢查鎖定(Double-Checked Locking):在多線程環(huán)境下,當(dāng)需要延遲初始化一個(gè)對(duì)象時(shí),為了避免重復(fù)初始化,常常使用雙重檢查鎖定。在這種情況下,需要使用volatile
關(guān)鍵字來(lái)確保對(duì)象在多線程環(huán)境中的可見(jiàn)性。例如:
public class Singleton {
private volatile static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
在上述代碼中,我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的單例模式。在getInstance()
方法中,我們使用雙重檢查鎖定來(lái)實(shí)現(xiàn)延遲初始化,確保只有在instance
為空時(shí)才創(chuàng)建新的Singleton
實(shí)例。
然而,在多線程環(huán)境下,由于指令重排序的存在,可能會(huì)導(dǎo)致以下問(wèn)題:
-
對(duì)象引用不為空但尚未初始化:在線程A執(zhí)行完
instance = new Singleton();
這一行之前,可能發(fā)生指令重排序,導(dǎo)致instance
的引用不為空,但是Singleton
實(shí)例的初始化還未完成。這樣,線程B在執(zhí)行return instance;
時(shí)就會(huì)得到一個(gè)尚未初始化的對(duì)象,導(dǎo)致錯(cuò)誤。 -
可見(jiàn)性問(wèn)題:指令重排序也可能導(dǎo)致線程B無(wú)法及時(shí)看到線程A的初始化操作。例如,線程A對(duì)
instance
的賦值可能被重排到線程A的后面執(zhí)行,從而線程B在讀取instance
時(shí)得到一個(gè)舊的引用,無(wú)法感知線程A的初始化操作。
為了解決這個(gè)問(wèn)題,需要在創(chuàng)建Singleton
實(shí)例時(shí)使用volatile
關(guān)鍵字來(lái)保證對(duì)象的可見(jiàn)性和禁止指令重排序。
?
送書(shū)活動(dòng)
《硅基物語(yǔ)·我是靈魂畫(huà)手》
?
當(dāng)AI遇上繪畫(huà),會(huì)打開(kāi)怎樣的奇妙世界?
用ChatGPT+Midjourney西出人類(lèi)的靈魂與夢(mèng)想
用StableDiffusion+D-ID畫(huà)出青春絢麗的渴望
激活每個(gè)人隱藏的繪畫(huà)天賦
人人都能成為頂尖繪畫(huà)大師
?
ChatGPT+Midjourncy+StableDiffusion+D-ID+RunwayGen-l
爆火軟件全流程協(xié)作
掌據(jù)AI繪商技巧
解鎖超全繪畫(huà)關(guān)鍵司
講解創(chuàng)作底層邏輯
一本書(shū)講透AI繪畫(huà)全流程
?
內(nèi)容簡(jiǎn)介
一本將AI繪畫(huà)講透的探秘指南,通過(guò)豐富的實(shí)踐案例操作,通俗易懂地講述AI繪畫(huà)的生成步驟生動(dòng)展現(xiàn)了AI繪畫(huà)的魔法魅力。從歷史到未來(lái),跨越百年時(shí)空,從理論到實(shí)踐,講述案例操作:從技術(shù)到哲學(xué),穿越多個(gè)維度,從語(yǔ)言到繪畫(huà),落地實(shí)戰(zhàn)演練。AI繪畫(huà)的誕生,引發(fā)了奇點(diǎn)降臨,點(diǎn)亮了AGI(通用人工智能),并涉及 Prompt、風(fēng)格,技術(shù)細(xì)節(jié)、多模態(tài)交互,AIGC等一系列詳細(xì)講解。讓您輕松掌握生圖技巧,創(chuàng)造出獨(dú)特的藝術(shù)作品,書(shū)寫(xiě)屬于自己的藝術(shù)時(shí)代。
?
?當(dāng)當(dāng)網(wǎng)鏈接:http://product.dangdang.com/29601870.html
?
?關(guān)注博主、點(diǎn)贊、收藏、
評(píng)論區(qū)評(píng)論
??即可參與送書(shū)活動(dòng)!?文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-624108.html
?
到了這里,關(guān)于JUC并發(fā)編程之volatile詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!