在java2之前,Java的內存模型老是從主存(共享內存)讀取變量,而在當前的Java內存模型下,每一個線程把變量保存到本地內存中,而不是直接在主存中進行讀寫。這就可能形成一個線程在主存中修改了一個變量的值,而另外一個線程還在繼續使用它在本地內存中的值。這個問題就是變量在多線程中的可見性問題。java
要解決這個問題,就須要把變量生命爲volatile,這就指示JVM,這個變量是不穩定的,每一個線程訪問該變量,它都到主內存中讀取,並且,當變量變化時,強迫線程將變化值回寫到主存。這樣在任什麼時候刻,兩個不一樣的線程老是看到某個成員變量的同一個值,這樣也保證了變量在多線程之間的可見性。多線程
看下面一個例子:ide
public class VolatileTest extends Thread { private static boolean flag =true; @Override public void run() { System.out.println("進入run了"); while (flag == true) {} System.out.println("線程被中止了!"); } public static void main(String[] args) { VolatileTest volatileTest=new VolatileTest(); volatileTest.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } volatileTest.flag=false; System.out.println("已經將isRunning賦值爲false"); } }
運行結果:性能
進入run了 已經將isRunning賦值爲false
能夠看到沒有「輸出線程被中止!」。線程進入了死循環。
這是由於main線程和volatileTest線程各自有一個工做線程,裏面有變量flag的拷貝,flag在這兩個線程之間不可見,即main線程修改了本身工做內存中flag的值,volatileTest線程並不知道。學習
如今咱們把flag前面加上關鍵字volatile,以下:線程
volatile private static boolean flag =true;
輸出就變成下面:code
進入run了 已經將isRunning賦值爲false 線程被中止了!
這是由於volatile保證了變量在不一樣線程之間的可見性。內存
若是咱們不想給flag前家volatile關鍵字,也能夠經過下面的方式解決:在whiel循環裏任意加上一個輸出語句或將該線程sleep一下。資源
volatile private static boolean flag =true; @Override public void run() { System.out.println("進入run了"); while (flag == true) { //法1:加一個循環語句 System.out.println("hello world"); //法2:將當前線程sleep一下 // try { // Thread.sleep(1000); // } catch (InterruptedException e) { // e.printStackTrace(); // } } System.out.println("線程被中止了!"); }
爲何呢?
由於JVM會盡力保證變量的可見性,即使這個變量沒有加volatile關鍵字只要CPU有時間,JVM就會盡力去保證變量值的更新。這與volatile關鍵字的不一樣之處在於,volatile會強制保證變量的可見性;而不加volatile關鍵字,JVM只會盡力去保證變量的可見性,若是CPU一直有其它的事情處理,它也就不會去保證了。最開始,CPU一直處於佔用狀態,JVM不會強制CPU其主存中取變量的最新值,因此死循環。後來加入輸出語句或sleep線程,CPU就有時間去保證變量的可見性,即從主內存中去變量flag的值。同步
問題:爲何加入輸出語句,CPU就有時間了?我也在繼續學習探究。