當一個變量定義爲volatile後,此變量對全部的線程具備可見性。這裏的可見性是指當一個線程修改了這個變量的值,新值對於其餘線程來講是能夠當即得知的。安全
每次使用volatile變量前都必須先從主內存刷新最新的值,這保證能看見其餘線程對變量所作的修改後的值。每次修改volatile變量後都必須同步回主內存,這保證其餘線程能夠看到本身對變量所作的修改。正是這兩條規則保證了volatile變量的可見性。多線程
volatile要求線程每次從共享內存中讀取變量,而不是私有內存中讀取。併發
volatile能夠禁止指令重排序優化,重排序是指編譯器和處理器爲了優化程序性能而對指令序列進行排序的一種手段。可是重排序也須要遵照必定規則,重排序操做不會對存在數據依賴關係的操做進行重排序。好比:a=1;b=a; 這個指令序列,因爲第二個操做依賴於第一個操做,因此在編譯時和處理器運行時這兩個操做不會被重排序。可是在多線程環境下,可能出現問題。以下示例:ide
Map configOptions; volatile boolean initialized = false; // 如下代碼在線程A中執行 // 線程A完成配置信息後,initialized改成true configOptions = new HashMap(); processConfigOptions(configOptions); initialized = true; // 如下代碼在線程B中執行 // 等待initialized爲true,表示線程A已完成配置 while (!initialized) { sleep(); } // 線程A完成配置後,線程B能夠開始doSomething(); doSomething();
initialized必須定義爲volatile,不然可能出現線程A先賦值initialized爲true,然後執行配置。性能
volatile保證了在賦值initialized以前,先執行完其前面的代碼。優化
volatile 的讀性能消耗與普通變量幾乎相同,可是寫操做稍慢,由於它須要在本地代碼中插入許多內存屏障指令來保證處理器不發生亂序執行。spa
以下示例,20個線程分別將變量race自加100次,運算結果race的值小於20000 。線程
public class VolatileTest { public static volatile int race; public static void increase() { race++; } private static final int threads_count = 20; public static void main(String[] args) { Thread[] threads = new Thread[threads_count]; for (int i=0; i<threads_count; i++) { threads[i] = new Thread( new Runnable() { @Override public void run() { for (int i=0; i<100; i++) increase(); } } ); threads[i].start(); } System.out.println(race); } }
示例中,race++不是一個原子操做,其步驟分解以下:code
1)從內存中取出race的值;blog
2)計算race的新值(加1);
3)將race的值寫入內存中。
race是volatile修飾的,這保證了其可見性,即全部的線程在使用該變量時,都是從共享內存中讀取。
但volatile不保證原子性,一個線程在進行第二步操做時,其它線程能夠讀取或修改race的值,這就出現了不一樣步的現象。
所謂原子性,是指一個操做或多個操做要麼所有執行完成且執行過程不被中斷,要麼就不執行。例如,若race++是原子性的,某個線程在執行該操做時,其餘線程不能干涉,必須等待其執行完畢。