(一)volatile的使用
1、使用場景
(1)狀態標識。用於實時指示某個重要性事件的發生,比如完成初始化或者停機。
volatile boolean toShutdown;
......
public void shutdown() { toShutdown = true; }
public void doWork() {
while (!toShutdown) {
// 執行操作
}
}
(2)對象安全發佈。volatile是處理對象安全發佈的其中一種方式,更多內容參加這裏。class Singleton{
private volatile static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if(instance==null) {
synchronized (Singleton.class) {
if(instance==null)
instance = new Singleton();
}
}
return instance;
}
}
經典的雙重檢查單例模式,volatile變量不會導致new對象的時候對象未初始化完成。
2、通常來說,使用volatile必須具備以下2個條件:
(1)對變量的寫操作不依賴於當前值
(2)該變量沒有包含在具有其他變量的不變式中
對於1,因爲volatile變量不保證原子性,一個線程執行i++的同時另一個線程獲取i的值,不一定能獲取到最新值。
對於2,
volatile int min = 0;
volatile int max = 10;
public boolean setA(int newMin) {
if (newMin > max) {
return false;
}
min = newMin;
return true;
}
public boolean setB(int newMax) {
if (newMax < min) {
return false;
}
max = newMax;
return true;
}
一個線程調用setA(8),一個線程調用setB(2),可能會得到min=8、max=2,這與最初代碼設計意圖不符合。
(二)原理分析
volatile實現內存可見性原理:
volatile Singleton instance = new Singleton();
instance變量在進行寫操作的時候,彙編下增加了lock指令。
lock指令的作用:
1.線程工作內存中的數據在進行寫操作的時候,會立即回寫到主內存中
2.回寫到主內存時,讓其他CPU中該數據的緩存失效,下次讀取需要重新從主內存中獲取
/**
* Volatile的使用測試
*
* @author peter_wang
* @create-time 2015-1-14 下午10:11:57
*/
public class ThreadVolatileDemo {
private boolean isStop = false;
private void changeStatus() {
isStop = !isStop;
}
/**
* @param args
*/
public static void main(String[] args) {
final ThreadVolatileDemo test = new ThreadVolatileDemo();
Thread thread1 = new Thread() {
@Override
public void run() {
System.out.println("嘗試退出開始");
while (true) {
//一直檢測isStop變量是否更新
if (test.isStop) {//A1
System.out.println("退出成功");
System.exit(0);
}
}
}
};
thread1.start();
try {
Thread.sleep(3000);
test.changeStatus();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
A1部分不斷循環重複讀取isStop值,但是讀取的是線程工作內存中的數據,而且不斷命中該數據,數據塊不易被替換,增加讀取到舊值的概率。修改isStop變量爲volatile,變量被修改後會立即回寫到主內存,A1讀取線程工作內存失效,從主內存中讀取到最新值。