在上一篇文章一男子給對象轉(zhuǎn)賬5000元馏慨,居然又退還了!中姑隅,我們學習了并發(fā)三大特性之一的原子性写隶,并對原子性問題進行分析。
這篇文章我們就一起來了解下可見性:
可見性
首先看下可見性的概念:
可見性就是指某一個線程修改了共享變量的值時讲仰,其他線程能夠立即得知這個修改慕趴。
什么?難道變量被修改了鄙陡,線程不應(yīng)該馬上讀取到的嗎冕房?為什么和我認知的不一樣呢?
好的趁矾,那么接下來讓我們帶著問題耙册,一起來搞懂可見性問題。
可見性問題
可見性問題的元兇就是 CPU 緩存毫捣,都怪 CPU 為程序性能優(yōu)化做的努力详拙,搞出這么多幺蛾子帝际。
關(guān)于 CPU 緩存可以閱讀:原來 CPU 為程序性能優(yōu)化做了這么多
首先在單核 CPU 上,是不存在可見性問題的饶辙,因為所有的線程都在一個 CPU 上執(zhí)行蹲诀,所有的線程都是操作同一 CPU 緩存,某一個線程修改了共享變量的值弃揽,另外的線程也可以馬上讀取到脯爪,因此是可見的。
如上圖所示矿微,Thread-0
和 Thread-1
都是在一個 CPU 緩存上進行操作痕慢,所以 Thread-0
修改了變量 flag
的值后,Thread-1
再去訪問變量 flag
冷冗,得到的一定是最新的 flag
值守屉。
然而在多核 CPU 上,由于每個 CPU 都有自己的緩存蒿辙,當多個不同線程運行在不同的 CPU 上時,這些線程操作的 CPU 緩存也是不同的滨巴,因此某一個線程對共享變量進行修改時思灌,另外的線程讀取到的不一定是最新值,也就不具有可見性了恭取。
如上圖所示泰偿,Thread-0
是在 CPU-0
上的緩存進行操作,Thread-1
是在 CPU-1
上的緩存進行操作蜈垮,所以 Thread-0
修改了變量 flag
的值后耗跛,Thread-1
再去訪問變量 flag
,得到的不一定是最新的 flag
值攒发,因此 Thread-0
對共享變量 flag
的修改對 Thread-1
是不可見的调塌。
下面用一個例子來看下可見性問題,創(chuàng)建一個 VisibilityTest
類惠猿,實現(xiàn) Runnable
接口羔砾,在 run()
方法中判斷 flag
是否為 true
,若為 true
則進行打印操作偶妖,主方法中啟動一個線程 thread
宛官,主線程等待 0.5 秒后疑苫,將 flag
的值設(shè)為 true
。
public class VisibilityDemo {
private static class VisibilityTest implements Runnable {
private boolean flag = false;
@Override
public void run() {
while (true) {
if (flag) {
System.out.println(Thread.currentThread().getName() + ":" + flag);
}
}
}
}
public static void main(String[] args) throws InterruptedException {
VisibilityTest visibilityTest = new VisibilityTest();
Thread thread = new Thread(visibilityTest);
thread.start();
// 等待線程啟動
Thread.sleep(500);
// 更新 flag 為 true
visibilityTest.flag = true;
System.out.println(Thread.currentThread().getName() + ":" + visibilityTest.flag);
}
}
發(fā)現(xiàn)輸出的結(jié)果為:main:true
,和我們想象的不太一樣摧扇,按道理 thread 應(yīng)該會持續(xù)打印出 Thread-0:true
的,但是并非如此蟹演,這也就驗證了我們剛才講的可見性問題。
那么如何解決可見性問題呢空扎?
可以采用同步的方式去解決或者使用 volatile 關(guān)鍵字也可以保證可見性。
關(guān)于 volatile 相關(guān)原理可以閱讀:你真的了解 volatile 關(guān)鍵字嗎润讥?
總結(jié)
本文學習了線程安全三大特性之中的可見性转锈,另外 CPU 緩存在提高程序性能的同時也帶來了可見性問題,只有我們理解了可見性的原理楚殿,才更容易去診斷并發(fā)編程中的 BUG撮慨。
參考
《Java并發(fā)編程實戰(zhàn)》
《深入理解Java虛擬機:JVM高級特性與最佳實踐》
《實戰(zhàn)Java高并發(fā)程序設(shè)計》
《Java多線程編程核心技術(shù)》
Java并發(fā)編程實戰(zhàn)