Java的Volatile實(shí)例用法及講解
在原子性、可見性、有序性中,volatile關(guān)鍵字主要在可見性中發(fā)揮作用。
volatile聲明的變量對所有線程來說是可見的,就是說當(dāng)變量的值發(fā)生改變的時(shí)候,其他線程可以立馬發(fā)現(xiàn)這個(gè)變化。
public class Main { private static boolean isRuning; private static int number; private static class ReaderThread extends Thread { public void run() { while (!isRuning) { System.out.println(number); } } } public static void main(String[] args) throws InterruptedException { new ReaderThread().start(); Thread.sleep(1000); number = 42; isRuning = true; Thread.sleep(1000); } }
應(yīng)該是由于編譯器優(yōu)化的存在,這里變量雖然沒有被volatile修飾,但是仍然對其他線程可見。。。。。
那為啥Volatile修飾的變量i++卻會(huì)有并發(fā)問題呢?
因?yàn)閕++并不是原子操作,
i++是有兩步操作的,比如 i=0; i++
1.讀取i=0
2.計(jì)算i+1,然后賦值給i
那么可能存在2個(gè)線程同時(shí)讀取到i=0,并計(jì)算出結(jié)果i=1然后賦值給I
那么就得不到預(yù)期結(jié)果i=2。
就是說雖然Volatile修飾的變量的變化可以被其他線程看到,但是如果同時(shí)去讀這個(gè)變量,然后進(jìn)行寫操作,則仍會(huì)導(dǎo)致線程安全問題。
更底層的原因是什么呢?
首先要知道Volatile修飾的變量會(huì)做兩件事(由lock指令完成):
- 1)將當(dāng)前處理器緩存行的數(shù)據(jù)寫回到系統(tǒng)內(nèi)存。
- 2)寫回內(nèi)存的操作會(huì)使在其他 CPU 里緩存了該內(nèi)存地址的額數(shù)據(jù)無效。
其他緩存會(huì)失效,不正好可以保證Volatile的原子性嗎?
然而并不是,
比如有T1 T2兩個(gè)線程進(jìn)行i++操作。
當(dāng)T1將變量加載到緩存,但是還沒進(jìn)行i++運(yùn)算,T2呢已經(jīng)加載完緩存并且已經(jīng)執(zhí)行完運(yùn)算,那這個(gè)時(shí)候T1緩存里的值就該變成無效的了。
但是Volatile并不是讓其他線程緩存無效以后就去重新加載主內(nèi)存中的值,如果這時(shí)候T2緩存的值已經(jīng)被放到寄存器并且cpu進(jìn)行計(jì)算了,那即使緩存無效也不會(huì)影響T2將計(jì)算的值回寫到主內(nèi)存中。
關(guān)于cpu執(zhí)行指令的過程可以參考https://blog.csdn.net/jizhu4873/article/details/84393905
當(dāng)一個(gè)變量定義為 volatile 之后,將具備兩種特性:
1.保證此變量對所有的線程的可見性,這里的“可見性”,如本文開頭所述,當(dāng)一個(gè)線程修改了這個(gè)變量的值,volatile 保證了新值能立即同步到主內(nèi)存,以及每次使用前立即從主內(nèi)存刷新。但普通變量做不到這點(diǎn),普通變量的值在線程間傳遞均需要通過主內(nèi)存(詳見:Java內(nèi)存模型)來完成。
2.禁止指令重排序優(yōu)化。有volatile修飾的變量,賦值后多執(zhí)行了一個(gè)“l(fā)oad addl $0x0, (%esp)”操作,這個(gè)操作相當(dāng)于一個(gè)內(nèi)存屏障(指令重排序時(shí)不能把后面的指令重排序到內(nèi)存屏障之前的位置),只有一個(gè)CPU訪問內(nèi)存時(shí),并不需要內(nèi)存屏障;(什么是指令重排序:是指CPU采用了允許將多條指令不按程序規(guī)定的順序分開發(fā)送給各相應(yīng)電路單元處理)。
volatile 變量的內(nèi)存可見性是基于內(nèi)存屏障(Memory Barrier)實(shí)現(xiàn)。
內(nèi)存屏障則由lock指令實(shí)現(xiàn)
以上就是本次介紹的全部知識點(diǎn)內(nèi)容,感謝大家對腳本之家的支持。
相關(guān)文章
Java中轉(zhuǎn)義字符反斜杠\的代替方法及repalceAll內(nèi)涵解析
這篇文章主要介紹了Java中轉(zhuǎn)義字符反斜杠\的代替方法及repalceAll內(nèi)涵解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08如何在?Spring?Boot?中使用?OpenAI?ChatGPT?API
這篇文章主要介紹了如何在Spring?Boot中使用OpenAI?ChatGPT?API,我們探索了 OpenAI ChatGPT API 以生成對提示的響應(yīng),我們創(chuàng)建了一個(gè) Spring Boot 應(yīng)用程序,它調(diào)用 API 來生成對提示的響應(yīng),需要的朋友可以參考下2023-08-08springboot+thymeleaf國際化之LocaleResolver接口的示例
本篇文章主要介紹了springboot+thymeleaf國際化之LocaleResolver的示例 ,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-115分鐘搭建SpringCloud Eureka服務(wù)注冊中心的實(shí)現(xiàn)
這篇文章主要介紹了5分鐘搭建SpringCloud Eureka服務(wù)注冊中心的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03