java volatile關(guān)鍵字作用及使用場景詳解
1. volatile關(guān)鍵字的作用:保證了變量的可見性(visibility)。被volatile關(guān)鍵字修飾的變量,如果值發(fā)生了變更,其他線程立馬可見,避免出現(xiàn)臟讀的現(xiàn)象。如以下代碼片段,isShutDown被置為true后,doWork方法仍有執(zhí)行。如用volatile修飾isShutDown變量,可避免此問題。
public class VolatileTest3 {
static class Work {
boolean isShutDown = false;
void shutdown() {
isShutDown = true;
System.out.println("shutdown!");
}
void doWork() {
while (!isShutDown) {
System.out.println("doWork");
}
}
}
public static void main(String[] args) {
Work work = new Work();
new Thread(work::doWork).start();
new Thread(work::doWork).start();
new Thread(work::doWork).start();
new Thread(work::shutdown).start();
new Thread(work::doWork).start();
new Thread(work::doWork).start();
new Thread(work::doWork).start();
}
}
出現(xiàn)臟讀時,運(yùn)行結(jié)果如下:

2. 為什么會出現(xiàn)臟讀?
Java內(nèi)存模型規(guī)定所有的變量都是存在主存當(dāng)中,每個線程都有自己的工作內(nèi)存。線程對變量的所有操作都必須在工作內(nèi)存中進(jìn)行,而不能直接對主存進(jìn)行操作。并且每個線程不能訪問其他線程的工作內(nèi)存。變量的值何時從線程的工作內(nèi)存寫回主存,無法確定。
3. happens-before規(guī)則的理解與勘誤
在網(wǎng)上查volatile關(guān)鍵字相關(guān)信息時,多篇博客提到了happens-before原則,個人對此原則的理解是:當(dāng)操作該volatile變量時,所有前序?qū)υ撟兞康牟僮鞫家淹瓿桑ㄈ绮淮嬖谝炎兏磳懟刂鞔娴那闆r),所有后續(xù)對該變量的操作,都未開始。僅此而已。
這里,我認(rèn)為網(wǎng)上很常見的一個理論對此理解有誤,如下圖。此觀點(diǎn)認(rèn)為,由于volatile變量flag的happens-before原則,所以A線程2處對其的寫操作一定先于B線程3處對其的讀操作。其實(shí)這種觀點(diǎn)是有邏輯缺陷的,如果存在一個C線程,先讀取flag的值,后寫入flag的值,那C線程的執(zhí)行時機(jī)是什么呢?如果還有其他D、E線程呢。。。對于這段代碼的正確理解是,只要3處拿到的flag是true,那么a的值一定是1,而不是0.因?yàn)関olition修飾的變量,處理器不會對其進(jìn)行重排序,所以1處對a的賦值,一定發(fā)生在2處對flag的賦值之前。如果flag不是volatile變量,那么1處和2處代碼的執(zhí)行順序是無法保證的(處理器的指令重排序),雖然大部分情況1會先于2執(zhí)行。happens-before原則約束的并不是多線程對同一變量的讀和寫操作之間的順序,而是保證讀操作時,前序所有對該變量的寫操作已生效(寫回主存)。

驗(yàn)證如下:
public class VolatileTest {
static class A {
int a = 0;
volatile boolean flag = false;
void writer() {
a = 1; //1
flag = true; //2
System.out.println("write");
}
void reader() {
if (flag) { //3
int i = a; //4
System.out.println("read true");
System.out.println("i is :" + i);
} else {
int i = a;
System.out.println("read false");
System.out.println("i is :" + i);
}
}
}
public static void main(String[] args) {
A aaa = new A();
new Thread(() -> aaa.reader()).start();
new Thread(() -> aaa.writer()).start();
}
}
運(yùn)行結(jié)果如下,在寫操作執(zhí)行之前,讀操作已完成

4. volatile關(guān)鍵字使用場景
注意:volatile只能保證變量的可見性,不能保證對volatile變量操作的原子性,見如下代碼:
public class VolatileTest2 {
static class A {
volatile int a = 0;
void increase() {
a++;
}
int getA(){
return a;
}
}
public static void main(String[] args) {
A a = new A();
new Thread(() -> {
for (int i = 0;i < 1000;i++) {
a.increase();
}
System.out.println(a.getA());
}).start();
new Thread(() -> {
for (int i = 0;i < 2000;i++) {
a.increase();
}
System.out.println(a.getA());
}).start();
new Thread(() -> {
for (int i = 0;i < 3000;i++) {
a.increase();
}
System.out.println(a.getA());
}).start();
new Thread(() -> {
for (int i = 0;i < 4000;i++) {
a.increase();
}
System.out.println(a.getA());
}).start();
new Thread(() -> {
for (int i = 0;i < 5000;i++) {
a.increase();
}
System.out.println(a.getA());
}).start();
}
}
運(yùn)行結(jié)果如下,volatile無法保證a++操作的原子性。

volatile正確的使用方法可參考:http://m.fzitv.net/article/166888.htm
以上就是本次介紹知識點(diǎn)的全部內(nèi)容,感謝大家對腳本之家的支持。
- 深入解析Java中volatile關(guān)鍵字的作用
- Java中volatile關(guān)鍵字的作用與用法詳解
- Java中Volatile關(guān)鍵字詳解及代碼示例
- Java中volatile關(guān)鍵字實(shí)現(xiàn)原理
- java volatile關(guān)鍵字使用方法及注意事項(xiàng)
- 談?wù)凧ava中Volatile關(guān)鍵字的理解
- 詳解Java面試官最愛問的volatile關(guān)鍵字
- 詳解Java線程編程中的volatile關(guān)鍵字的作用
- Java里volatile關(guān)鍵字是什么意思
- Java中的volatile關(guān)鍵字經(jīng)典應(yīng)用場景和常見問題
相關(guān)文章
java反射調(diào)用get/set方法實(shí)現(xiàn)
本文文章介紹了在Java中使用反射調(diào)用get/set方法時,如何通過Introspector和PropertyDescriptor來更優(yōu)雅地處理,下面就來詳細(xì)的介紹一下,感興趣的可以了解一下2025-12-12
Springboot集成Spring Security實(shí)現(xiàn)JWT認(rèn)證的步驟詳解
這篇文章主要介紹了Springboot集成Spring Security實(shí)現(xiàn)JWT認(rèn)證的步驟詳解,幫助大家更好的理解和使用springboot,感興趣的朋友可以了解下2021-02-02
Spring配置文件解析之BeanDefinitionReader詳解
這篇文章主要介紹了Spring配置文件解析之BeanDefinitionReader詳解,ApplicationContext.xml配置文件解析成Document對象,真正對xml中元素解析的類是在BeanDefinitionDocumentReader的實(shí)現(xiàn)類中來完成的,需要的朋友可以參考下2024-02-02
java HttpServletRequest和HttpServletResponse詳解
這篇文章主要介紹了java HttpServletRequest和HttpServletResponse詳解的相關(guān)資料,需要的朋友可以參考下2016-12-12
Mybatis的association使用子查詢結(jié)果錯誤的問題解決
本文主要介紹了Mybatis的association使用子查詢結(jié)果錯誤的問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-07-07

