Java中的關(guān)鍵字volatile詳解
volatile關(guān)鍵字經(jīng)常用來(lái)修飾變量。不過(guò),volatile本身很容易被誤用。本篇就介紹一下volatile的原理和使用方式。
在介紹volatile關(guān)鍵字原理前,我們首先要了解JVM運(yùn)行時(shí)的內(nèi)存分配邏輯。

對(duì)于成員變量i,它存儲(chǔ)在堆內(nèi)存中。每個(gè)線程在運(yùn)行時(shí)都會(huì)有一個(gè)自己的線程棧,線程如果要訪問(wèn)類(lèi)的成員變量i,會(huì)通過(guò)引用獲取到堆中變量i實(shí)際的值10,然后把這個(gè)變量值拷貝到自己的棧內(nèi)存中,作為一個(gè)變量副本,之后線程便不再會(huì)與堆中的變量有實(shí)際聯(lián)系。每個(gè)線程都有一個(gè)自己的本地副本,相互隔離。線程訪問(wèn)自己棧內(nèi)存的效率比訪問(wèn)堆的效率高。線程對(duì)變量i值的修改,只會(huì)修改自己線程副本中的值,修改結(jié)束后,在線程退出前,會(huì)把自己線程副本中的值,刷新到堆中。
保證內(nèi)存可見(jiàn)性
對(duì)于如下代碼:
public class VolatileTest implements Runnable{
//volatile
private static boolean flag = false;
@Override
public void run() {
while (!flag){
System.out.println(Thread.currentThread().getName() +"執(zhí)行中");
}
System.out.println(Thread.currentThread().getName() +"執(zhí)行完畢");
}
//main線程
public static void main(String[] args) throws InterruptedException {
new Thread(new VolatileTest(), "支線程Volatile").start();
Thread.sleep(1000);
flag = true;
}
}
大多數(shù)時(shí)候可以正常中斷,但是一旦發(fā)送異常,便會(huì)導(dǎo)致線程死循環(huán)。所以需要在flag標(biāo)志上加一個(gè)volatile關(guān)鍵字。對(duì)于加了volatile關(guān)鍵字的變量值,線程1修改了這個(gè)值的話,會(huì)強(qiáng)制將修改值直接寫(xiě)入堆內(nèi)存中,其他線程各自線程棧中的變量副本無(wú)效,只能去堆中取最新的變量值。多個(gè)線程之間的內(nèi)存可見(jiàn)得以保證。
值得注意的是,volatile關(guān)鍵字不能保證原子性。
private volatile int i; i++;
i ++ 這個(gè)操作涉及到獲取值,自增和賦值3部分。無(wú)法直接完成。上面想要以volatile來(lái)實(shí)現(xiàn)原子性的寫(xiě)法是錯(cuò)誤的。
禁止指令重排
現(xiàn)代JVM對(duì)代碼的執(zhí)行順序有一定的優(yōu)化。例如:
int a = 4; int b = 5; int c = a + b;
上面3條指令進(jìn)過(guò)JVM優(yōu)化以后,時(shí)間的執(zhí)行順序不一定是從上到下,有可能是 第二條--->第一條-->第三條??傊粫?huì)影響最終執(zhí)行結(jié)果。
但是在多線程情況下,如下代碼就會(huì)有風(fēng)險(xiǎn):
//線程1:
context = loadContext();
inited = true;
//線程2:
while(!inited ){
}
doSomething(context);
線程1的兩條語(yǔ)句之間沒(méi)有依賴(lài)性,經(jīng)過(guò)指令重排后,有可能inited置為true以后,context還沒(méi)有初始化。線程2發(fā)現(xiàn)inited為true,以為初始化完成,結(jié)束循環(huán),用時(shí)間還沒(méi)有初始化的context去執(zhí)行doSomething()方法。報(bào)錯(cuò)。所以我們可以用volatile關(guān)鍵字修飾inited,保證context初始化。
以上所述是小編給大家介紹的Java中的關(guān)鍵字volatile詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- Java并發(fā)編程volatile關(guān)鍵字的作用
- Java多線程并發(fā)編程 Volatile關(guān)鍵字
- Java并發(fā)編程:volatile關(guān)鍵字詳細(xì)解析
- 詳細(xì)分析java并發(fā)之volatile關(guān)鍵字
- 深入了解Java中Volatile關(guān)鍵字
- Java Volatile關(guān)鍵字同步機(jī)制詳解
- Java Volatile關(guān)鍵字實(shí)現(xiàn)原理過(guò)程解析
- 簡(jiǎn)單了解java volatile關(guān)鍵字實(shí)現(xiàn)的原理
- 一文精通Java中的volatile關(guān)鍵字
- Java面試官最喜歡問(wèn)的關(guān)鍵字之volatile詳解
- 詳解Java面試官最?lèi)?ài)問(wèn)的volatile關(guān)鍵字
- Java中Volatile關(guān)鍵字詳解及代碼示例
- Java并發(fā)編程——volatile關(guān)鍵字
相關(guān)文章
SpringBoot?+?Redis如何解決重復(fù)提交問(wèn)題(冪等)
在開(kāi)發(fā)中,一個(gè)對(duì)外暴露的接口可能會(huì)面臨瞬間的大量重復(fù)請(qǐng)求,本文就介紹了SpringBoot + Redis如何解決重復(fù)提交問(wèn)題,具有一定的參考價(jià)值,感興趣的可以了解一下2021-12-12
Java8中利用stream對(duì)map集合進(jìn)行過(guò)濾的方法
這篇文章主要給大家介紹了關(guān)于Java8中利用stream對(duì)map集合進(jìn)行過(guò)濾的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07
解決Hmily與Feign沖突報(bào)錯(cuò) NullPointerException的問(wèn)題
這篇文章主要介紹了解決Hmily與Feign沖突報(bào)錯(cuò) NullPointerException的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
解讀maven項(xiàng)目中Tomcat10與JSTL的問(wèn)題匯總(Debug親身經(jīng)歷)
這篇文章主要介紹了解讀maven項(xiàng)目中Tomcat10與JSTL的問(wèn)題匯總(Debug親身經(jīng)歷),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
詳解Java并發(fā)編程中的優(yōu)先級(jí)隊(duì)列PriorityBlockingQueue
PriorityBlockingQueue是Java中實(shí)現(xiàn)了堆數(shù)據(jù)結(jié)構(gòu)的線程安全的有界阻塞隊(duì)列。本文將會(huì)深入解讀PriorityBlockingQueue的源碼實(shí)現(xiàn),感興趣的可以了解一下2023-05-05
java jackson 將對(duì)象轉(zhuǎn)json時(shí),忽略子對(duì)象的某個(gè)屬性操作
這篇文章主要介紹了java jackson 將對(duì)象轉(zhuǎn)json時(shí),忽略子對(duì)象的某個(gè)屬性操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10
JDBC PreparedStatement Like參數(shù)報(bào)錯(cuò)解決方案
這篇文章主要介紹了JDBC PreparedStatement Like參數(shù)報(bào)錯(cuò)解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10

