一:概念java
volatile關鍵字是一個輕量級的線程同步,它能夠保證線程之間對於共享變量的同步,假設有兩個線程a和b,緩存
它們均可以訪問一個成員變量,當a修改爲員變量的值的時候,要保證b也可以取得成員變量最新的值,程序的安全
內存模型是這樣的,程序運行時,成員變量的值被加載到內存中,若是線程a運行時,會把變量的值拷貝到cpu分配線程
給a的高速緩存區,就是內存的一個副本,線程b運行時,會把變量拷貝到cpu分配給b的高速緩存區,正常狀況下,對象
a線程修改爲員變量時,會將高速緩存中的值寫入主存,而後b線程運行時讀取主存中值到緩存,可是不是強制性的,blog
使用volatile關鍵字就是強制性。內存
1:將高速緩存強制寫入主內存同步
2:會使b線程高速緩存標記失效io
二:比較經典的一個示例class
t1線程先啓動,而後一直打印‘i love u’,這時t2線程啓動,將flag變量的值修改成true,而後t1線程的執行終止,若是flag變量不加volatile修飾,
出現死循環的機率是存在的,可是比較低,若是加volatile,會強制t2線程修改主內存中flag的值,並且t1線程高速緩存標記會失效,能夠保證
必定可以終止t1程序的執行
/** * */ package com.day2; /** * @author Administrator * */ public class ListAdd1 { private boolean flag; public static void main(String[] args) { ListAdd1 list = new ListAdd1(); //線程1 Thread t1 = new Thread("t1"){ public void run(){ while(!list.flag){ System.out.println("i love u"); } } }; //線程2 Thread t2 = new Thread("t2"){ public void run(){ list.flag = true; } }; t1.start(); //保證t1線程先啓動 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } t2.start(); } }
private volatile boolean flag;
可是volatile並不能保證操做的原子性,線程搶到cpu的時間片,修改高速緩存的值,寫入主內存這幾個過程不是原子的,
int i = 0;
i = i+1;
若是線程1在搶到cpu的時間片以後,尚未修改高速緩存的值,而後線程2也讀取了主內存中緩存的值i = 0,而後執行加1,
寫入高速緩存,線程1以前讀取緩存中的值也是0,而後執行加1,寫入主內存,這樣就出現問題了,因此使用volatile不能
保證線程安全問題。
以下示例:
啓動10個線程,count初始值爲0,正常狀況,10個線程個循環1000次,最後的count值應該爲10000,可是不是,這個值
是隨機的。
/** * */ package com.day2; /** * @author Administrator * */ public class ListAdd2 { private volatile int count; public static void main(String[] args) { ListAdd2 list = new ListAdd2(); System.out.println(list.count); for(int i=0;i<10;i++){ new Thread("t"+i){ public void run(){ for(int j=0;j<1000;j++){ list.count++; } } }.start(); } System.out.println(list.count); } }
若是想確保線程安全,那麼必須使用synchronized鎖
synchronized (list) { list.count++; }
由於10個線程訪問的是同一個實例,因此使用對象鎖就能夠了。