做者:畢來生
微信:878799579
JUC全稱 java.util.concurrent 是在併發編程中很經常使用的實用工具類java
一、若是一個變量被volatile關鍵字修飾,那麼這個變量對全部線程都是可見的。
二、若是某條線程修改了被Volatile修飾的這個變量值,修改後的值對於其餘線程來時是當即可見的。
三、並非通過Volatile修飾過的變量在多線程下就是安全的
四、多線程間可使用SynchronousQueue或者Exchanger進行數據之間傳遞編程
內存可見性(Memory Visibility)是指當某個線程正在使用對象狀態 而另外一個線程在同時修改該狀態,須要確保當一個線程修改了對象 狀態後,其餘線程可以看到發生的狀態變化。
可見性錯誤是指當讀操做與寫操做在不一樣的線程中執行時,咱們沒法確保執行讀操做的線程能適時地看到其餘線程寫入的值,有時甚至是根本不可能的事情。
原理同CAS原理相同,不懂的同窗能夠自行百度,附上一張CAS演示圖供你們參考緩存
經過線程來修改變量count的值,使用Volatile關鍵字修飾和不使用Volatile修飾count變量結果對比。安全
首先咱們來看一下經過內部類實現Runnable,變量<font color="red">使用Volatile關鍵字</font>修飾演示以及結果微信
package org.bilaisheng.juc; /** * @Author: bilaisheng * @Wechat: 878799579 * @Date: 2019/1/1 16:29 * @Todo: 經過內部類實現Runnable,變量使用Volatile關鍵字修飾演示 * @Version : JDK11 , IDEA2018 */ public class NoVolatileTest{ public static void main(String[] args) { NoVolatileThread noVolatileThread = new NoVolatileThread(); new Thread(noVolatileThread).start(); while (true){ if(noVolatileThread.isFlag()){ System.out.println("flag 此時爲true !"); break; } } } } class NoVolatileThread implements Runnable{ private boolean flag = false; @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } flag = true; System.out.println(Thread.currentThread().getName() + " flag = " + flag); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } }
運行結果以下圖所示:多線程
接下來咱們來看一下經過內部類實現Runnable,變量<font color="red">不使用Volatile關鍵字</font>修飾演示以及結果併發
package org.bilaisheng.juc; /** * @Author: bilaisheng * @Wechat: 878799579 * @Date: 2019/1/1 16:53 * @Todo: 經過內部類實現Runnable,變量使用Volatile關鍵字修飾演示 * @Version : JDK11 , IDEA2018 */ public class VolatileTest{ public static void main(String[] args) { VolatileThread volatileThread = new VolatileThread(); new Thread(volatileThread).start(); while (true){ // if的判斷volatile保證當時確實正確,而後線程a可能處於休眠狀態, // 線程b也判斷不存在,b線程就new了一個。 // 而後a線程wake up,據需執行new volatile獲取最新值。 if(volatileThread.isFlag()){ System.out.println("flag 此時爲true !"); break; } } } } class VolatileThread implements Runnable{ private volatile boolean flag = false; @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } flag = true; System.out.println(Thread.currentThread().getName() + " flag = " + flag); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } }
運行結果以下圖所示:ide
經過對比咱們發如今經過Volatile修飾和不經過Volatile修飾的變量,輸出結果居然會有些誤差。究竟是爲何呢?高併發
咱們逐步拆解上面代碼執行步驟:工具
一、針對於不使用Volatile關鍵字修飾變量:
二、針對於使用Volatile關鍵字修飾變量:
可見性:被Volatile修飾的變量能夠立刻刷新主內存中的值,保證其餘線程在獲取時能夠獲取最新值,全部線程看到該變量的值均相同。
輕量級的synchronized,高併發下保證變量的可見性。
一、頻繁刷新主內存中變量,可能會形成性能瓶頸
二、不具有操做的原子性,不適合在對該變量的寫操做依賴於變量自己本身。例如i++,並不能經過volatile來保證原子性