Java并發(fā)volatile可見性的驗(yàn)證實(shí)現(xiàn)
普通讀 無法及時(shí)獲得 主內(nèi)存變量
public class volatileTest {
static boolean flag = false;//非volatile變量
public static void main(String[] args) throws Exception {
new Thread(new Runnable() {
@Override
public void run() {
while(!flag){
};
}
}).start();
Thread.sleep(100);
flag = true;
System.out.println("主線程運(yùn)行完畢");
}
}
主線程已經(jīng)修改了flag為true,但子線程一直不會(huì)退出循環(huán),因?yàn)樽泳€程一直沒有同步到 主內(nèi)存中的變量的值。

截圖可見程序一直沒有退出,使用dump threads后:
"Thread-0" #12 prio=5 os_prio=0 tid=0x0000000022d89800 nid=0x168 runnable [0x00000000248df000]
java.lang.Thread.State: RUNNABLE
at volatileTest$1.run(volatileTest.java:10)
at java.lang.Thread.run(Thread.java:745)
volatile讀 及時(shí)獲得 主內(nèi)存變量
public class volatileTest {
static volatile boolean flag = false;//volatile變量
public static void main(String[] args) throws Exception {
new Thread(new Runnable() {
@Override
public void run() {
while(!flag){
};
}
}).start();
Thread.sleep(100);
flag = true;
System.out.println("主線程運(yùn)行完畢");
}
}
加了一個(gè)volatile關(guān)鍵字,子線程就能檢測(cè)到flag的變化了。子線程會(huì)退出。
普通讀+sleep
public class volatileTest {
static boolean flag = false;//非volatile變量
public static void main(String[] args) throws Exception {
new Thread(new Runnable() {
@Override
public void run() {
while(!flag){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}
}).start();
Thread.sleep(100);
flag = true;
System.out.println("主線程運(yùn)行完畢");
}
}
加了sleep,子線程會(huì)退出。
其實(shí)就算變量不是volatile的,JVM也會(huì)盡量去保證可見性。最開始的例子,由于CPU一直執(zhí)行循環(huán),沒有其他時(shí)間來獲取 主內(nèi)存中的變量的最新值,但加了sleep后,CPU就有時(shí)間去獲取 主內(nèi)存中的東西了。
普通讀+同步塊
public class volatileTest {
static boolean flag = false;//非volatile變量
static Object sync = new Object();
public static void main(String[] args) throws Exception {
new Thread(new Runnable() {
@Override
public void run() {
while(!flag){
synchronized (sync) {}//隨便synchronized一個(gè)對(duì)象
//synchronized (this)也可以
};
}
}).start();
Thread.sleep(100);
flag = true;
System.out.println("主線程運(yùn)行完畢");
}
}
加了同步塊,子線程會(huì)退出。
這是因?yàn)閟ynchronized具體過程是:
- 獲得同步鎖;
- 清空工作內(nèi)存;
- 從主內(nèi)存拷貝對(duì)象副本到工作內(nèi)存;
- 執(zhí)行代碼(計(jì)算或者輸出等);
- 刷新主內(nèi)存數(shù)據(jù);
- 釋放同步鎖。
簡(jiǎn)單的說,synchronized進(jìn)入時(shí),會(huì)將 主內(nèi)存中最新的變量,拷貝進(jìn) 自己線程 的工作內(nèi)存。synchronized退出時(shí),會(huì)把 自己線程的工作內(nèi)存的變量 弄進(jìn) 主內(nèi)存中。
同步塊 遭遇 鎖消除
public class volatileTest {
static boolean flag = false;//非volatile變量
public static void main(String[] args) throws Exception {
new Thread(new Runnable() {
@Override
public void run() {
while(!flag){
synchronized (new Object()){}
};
}
}).start();
Thread.sleep(100);
flag = true;
System.out.println("主線程運(yùn)行完畢");
}
}
子線程不會(huì)退出。
原因是:synchronized (new Object()){}中這個(gè)Object永遠(yuǎn)不可能逃逸到同步塊以外去,所以同步操作其實(shí)根本不需要執(zhí)行了,既然沒有執(zhí)行同步,那么相當(dāng)于這里是啥也沒有。
普通讀+System.out.println
public class volatileTest {
static boolean flag = false;
public static void main(String[] args) throws Exception {
new Thread(new Runnable() {
@Override
public void run() {
while(!flag){
System.out.println("子線程running");
};
}
}).start();
Thread.sleep(100);
flag = true;
System.out.println("主線程運(yùn)行完畢");
}
}
加了System.out.println,子線程會(huì)退出。
因?yàn)閛ut這個(gè)PrintStream實(shí)例的println實(shí)現(xiàn)是:
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
因?yàn)樗灿型綁K。
到此這篇關(guān)于Java并發(fā)volatile可見性的驗(yàn)證實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Java并發(fā)volatile驗(yàn)證內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot Event 事件如何實(shí)現(xiàn)異步延遲執(zhí)行
這篇文章主要介紹了Spring Boot Event 事件如何實(shí)現(xiàn)異步延遲執(zhí)行問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02
java實(shí)現(xiàn)給出分?jǐn)?shù)數(shù)組得到對(duì)應(yīng)名次數(shù)組的方法
這篇文章主要介紹了java實(shí)現(xiàn)給出分?jǐn)?shù)數(shù)組得到對(duì)應(yīng)名次數(shù)組的方法,涉及java針對(duì)數(shù)組的遍歷、排序及運(yùn)算的相關(guān)技巧,需要的朋友可以參考下2015-07-07
Java常見基本數(shù)據(jù)結(jié)構(gòu)概覽
這篇文章主要介紹了Java常見的幾種基本數(shù)據(jù)結(jié)構(gòu),包括其含義和用法,,需要的朋友可以參考下2017-09-09

