瞭解過多線程的我們,對synchorized,sleep和valatile都比較瞭解,但是當這三個名詞和“Java變量得可見性”的話題聯繫在一起不知道大家是否還可以保持大腦清晰???
最近看到一個關於Java變量可見性的問題,感覺比較新鮮就查了一些資料,下面分享給大家:
首先給大家看一段關於多線程的執行代碼:
package com.test;
import java.util.concurrent.TimeUnit;
public class test1 {
private static boolean is = true;
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while(test1.is){
i++;
1 //synchronized (this) { } 會強制刷新主內存的變量值到線程棧?
2 //System.out.println("1"); println 是synchronized 的,會強制刷新主內存的變量值到線程棧?
3 //sleep 會從新load主內存的值?
// try {
// TimeUnit.MICROSECONDS.sleep(1);
// }catch (InterruptedException e) {
// e.printStackTrace();
// }
}
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
is = false; //設置is爲false,使上面的線程結束while循環
}
}).start();
}
}
在運行當前代碼時,我們會發現程序可以正常運行,但是如果取消註釋行(1-3)中的任意一行,程序都會終止,我們不免產生疑問:爲什麼?synchronized會強制刷新主內存的變量值到線程棧??那麼sleep是幹嘛的?
下面一起來解決代碼執行過程中產生的問題:
Q1:爲什麼註釋代碼後程序不會終止?
A1:因爲 boolean is=true 的變量值被前面線程(簡稱線程A)加載到自己的工作內存,在後面的線程(簡稱線程B)改變 boolean is=false 之後不一定會立馬寫入主存(不過這道題中應該會馬上寫入主存,因爲線程執行完 is=false之後線程就要退出了),即便立馬寫入了主存後線程A也不一定馬上load到工作內存中,所以程序一直不會終止?這個是我們大多數人想到的,但其實JVM針對現在的硬件水平已經做了很大程度的優化,基本上很大程度的保障了工作內存和主內存的及時同步,相當於默認使用了volatile。但只是最大程度!在CPU資源一直被佔用的時候,工作內存與主內存中間的同步,也就是變量的可見性就會不那麼及時!後面會驗證結論。
Q2:爲什麼取消註釋中的任意一個代碼塊(1,2,3)程序就會終止??
A2:行號爲1,2的代碼有一個共同點:都涉及到了synchronized同步鎖,那麼是否會像小編在文章開始時產生的質疑“synchronized會強制刷新主內存的變量值到線程棧”?或者說是sleep方法會刷新主存的變量值到線程棧嗎?
事實上,我們都知道synchronized只會保證在同步塊中的變量的可變性,而is變量並不在該同步塊中,所以顯然不是這個導致的;那麼我們可以嘗試在“i++” 的代碼後面加上下面的代碼:
for(int k=0;k<100000;k++){
new Object();
}
這時,我們重新運行程序,程序會立刻終止,這又是爲什麼?
因爲我們知道當CPU在被佔用的時候,數據的可見性得不到很好的保證。就像上面的例子中,沒有添加代碼之前,程序會一直循環做i++操作,所以CPU會被運算佔用;而對於大量的new Object()操作來說,CPU已經不是主要站時間的操作,真正的耗時應該在內存的分配上(因爲CPU的處理速度明顯快過內存,不然也不會有CPU的寄存器了),所以CPU空閒後會遵循jvm優化基準,儘可能快的保證數據的可見性,從而從主存將is變量同步到工作內存中,最終導致程序的結束,這也就是sleep()方法雖然沒有涉及到同步操作,卻依然可以讓程序終止的原因,因爲sleep()方法會釋放CPU,但並不會釋放鎖;
後記
➷ 知識擴展:
volatile:此關鍵字保證了變量在線程的可見性,所有線程訪問由volatile修飾的變量,都必須從主存中讀取後操作,並在工作內存修改後立即寫回主存,保證了其他線程的可見性,同樣效果的關鍵字還有final。
synchronized:所有同步操作都必須保證 1、原子性 2、可見性,所以在同步塊中發生的變化會立馬寫回主存
sleep:此方法只會讓出CPU執行時間,並不會釋放鎖;
➷ 寄語
技術在於不斷的發現和總結,我們需要將自己輸入的知識進行整理後輸出,這樣才能對輸入的知識更好的理解;以上純屬個人理解,如有錯誤,請大家指正;