相信大多數Java程序員都學習過volatile這個關鍵字的用法。百度百科上對volatile的定義:java
volatile是一個類型修飾符(type specifier),被設計用來修飾被不一樣線程訪問和修改的變量。volatile的做用是做爲指令關鍵字,確保本條指令不會因編譯器的優化而省略,且要求每次直接讀值。程序員
可能有不少剛學Java的朋友們看了上面這段很是籠統的描述後仍然以爲雲裏霧裏的。ide
下面咱們就用一個具體的例子來學習volatile的用法。學習
看這個例子:優化
public class ThreadVerify { public static Boolean stop = false; public static void main(String args[]) throws InterruptedException { Thread testThread = new Thread(){ @Override public void run(){ int i = 1; while(!stop){ //System.out.println("in thread: " + Thread.currentThread() + " i: " + i); i++; } System.out.println("Thread stop i="+ i); } } ; testThread.start(); Thread.sleep(1000); stop = true; System.out.println("now, in main thread stop is: " + stop); testThread.join(); } }
這段代碼在主線程的第二行定義了一個布爾變量stop, 而後主線程啓動一個新線程,在線程裏不停得增長計數器i的值,直到主線程的布爾變量stop被主線程置爲true才結束循環。操作系統
主線程用Thread.sleep停頓1秒後將布爾值stop置爲true。線程
所以,咱們指望的結果是,上述Java代碼執行1秒鐘後中止,而且打印出1秒鐘內計數器i的實際值。設計
然而,執行這個Java應用後,你發現它進入了死循環,在任務管理器裏發現這個Java程序CPU佔用率飆升。code
緣由是什麼呢?讓咱們溫習下計算機專業課操做系統中講過的內存模型的知識。內存
以Java內存模型爲例,Java內存模型分爲主內存(main memory)和工做內存(work memory)。主內存內的變量由全部線程共享,每一個線程擁有本身的工做內存,裏面的變量包含了線程局部變量。主內存中的變量若是被線程使用到,則線程的工做內存會維護一份主內存變量的副本拷貝。
線程對變量的全部讀寫操做必須在工做內存中進行,不能直接操做主內存中的變量。不一樣線程之間也沒法直接訪問對方的工做內存。線程間變量的傳遞需經過主內存來完成。線程、主內存、工做內存三者之間的交互關係以下圖:
若是線程在本身的執行代碼裏修改了定義在主線程(主內存)中的變量,修改直接發生在線程的工做內存裏,而後在某個時刻(Java程序員沒法控制這個時刻,而是由JVM調度的),這個修改從工做內存寫回到主內存。
回到咱們的例子。儘管主線程修改了stop變量,可是僅僅修改了主內存中的值,而操做計數器的線程的工做內存裏的stop變量仍是舊的值,始終爲false。所以這個線程陷入了死循環。
知道了原理,解決方案就很簡單了。在stop變量前加上關鍵字volatile進行修飾,這樣在計數器線程裏每次讀取stop的值時,volatile會強制該線程從主內存讀取,而不是從當前線程的工做內存讀取。這樣就避免了死循環。下圖顯示1秒鐘以後,計數器執行了14億次。
要獲取更多Jerry的原創技術文章,請關注公衆號"汪子熙"或者掃描下面二維碼: