1. volatile的做用java
a.volatile關鍵字能夠簡單保持賦值和返回操做的原子性,弱同步。編程
好比:讀取和寫入long和double不是原子性的操做,jvm會把64位(long和double)的讀取和寫入看成兩個分離的32位操做來執行。這就產生了在一個讀取和寫入操做中間發生上下文切換,從而致使不一樣的任務能夠看到不正確的結果 ,可是若是當你定義long和double變量時,使用volatile關鍵字,就會獲得(賦值和返回操做)原子性。 緩存
b.volatile還能夠確保應用中的可視性。jvm
若是你將一個域聲明爲volatile,那麼只要對這個域產生了寫操做,那麼全部的讀操做就均可以看到這個修改。即使使用了本地緩存,狀況也確實若是,volatile域會當即被寫入主存,而讀取操做就發生在主存中。性能
其實同步也會致使向主存中刷新,所以若是一個域徹底有synchronnized方法或語句塊來防禦,那就沒必要將其設置爲是volatile。學習
若是你將一個域定義爲volatile,那麼它會告訴編譯器不要執行任何移除讀取和寫入操做的優化,這些操做的目的是用線程中的局部變量維護對這個域的精準同步。優化
注意:若是一個域的值依賴於它以前的值時,(例如遞增一個計數器),volatile就沒法操做了,示例:線程
package mutex.conflict; import java.util.concurrent.*; public class AtomicityTest implements Runnable { private int i = 0; public int getValue() { return i; } private synchronized void evenIncrement() { i++; i++; } public void run() { while(true) evenIncrement(); } public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); AtomicityTest at = new AtomicityTest(); exec.execute(at); while(true) { int val = at.getValue(); if(val % 2 != 0) { System.out.println(val); System.exit(0); } } } } /* Output: (Sample) 191583767 *///:~
輸出:code
191583767blog
該程序的主要目的是找到奇數值並終止。
儘管return i是原子性操做,可是缺乏同步使其數值能夠在處於不穩定的中間狀態時被讀取。除此以外,因爲i也不是volatile的,所以還存在可視性問題。(不過這兒就是i是volatile也不起做用)因此getValue()和eventIncrement()必須是synchronized的。就能夠解決問題。
學習總結自《java編程思想》
---------------------------------------------更新於2019年6月9號--------------------------------------
如今看了這篇文章,感受總結的太淺顯了。下面作一個深刻的總結。
volatile的主要做用是:保證了變量修改的可見性,即一個線程對該變量的修改,對另一個線程立馬可見。
那麼問題來了,爲何不用這個關鍵字修飾,就不可用呢?主要是兩個緣由
1.緩存致使內存不可見。
如今的cup的運行速度遠遠高於內存的讀寫速度, 爲了讓cpu沒必要由於等待讀寫內存數據而空閒。如今cpu和內存以前都會增長一道高速緩存cache, 而多核的cpu的每一個核心都有本身的cache。當數據更新只在本身的cache, 尚未更新到主存中,別的線程這個時候是不可見的。
2. 指令重排序致使不可見
指令重排序是指: cpu採用了容許將多條指令不按程序規定的順序分開發送給各相應電路單元處理。在應用程序就是,提升代碼執行性能,指令的執行順序被編譯器或者運行時環境調整順序的現象。重排序分爲編譯期重排序和運行期重排序,編譯期重排序指的是編譯期在編譯源代碼的時候對代碼的執行順序進行分析,在不管怎麼重排序,不影響最終的執行結果的狀況下,對代碼進行重排序,以提升代碼執行性能。運行期重排序,指的是爲了提升指令流水並行執行能力,系統對機器指令執行順序的調整。
重排序是怎麼致使不可見的呢?
因爲指令順序的調整,線程B讀取某個變量的時候,線程1可能尚未進行寫入操做,雖然代碼順序是線程1寫入代碼在前面。
volatile的原理:
volatile不容許線程內部緩存,直接寫入主存,而讀取的時候,也是直接讀取主存,不讀取本身的cpu cache。
volatile 不容許指令重排。