1. 程式人生 > >volatile關鍵字解析(一)

volatile關鍵字解析(一)

== 輕量 except 問題 while pan 輕量級 當前 指令重排

引起線程並發問題,可以簡單的總結為以下三條:

  • 原子性問題
  • 可見性問題
  • 有序性問題(重排序問題)

原子性問題

什麽是原子性?

原子性,即一個操作或者多個操作,要麽全部執行並且執行過程中不會被任何因素打斷,要麽全部都不執行。

如常見的銀行轉賬、count++操作等,都必須具備原子性才能保證不出現意外。

A向B轉賬100元,需要保證兩步:A賬戶減100,B賬戶加100,如果有任何一個發生意外,總有不滿意的一方,A賬戶減了100,B賬戶沒有加100,客戶肯定不滿意;A賬戶由於某種原因沒有減100,但是B賬戶卻加了100,銀行此時肯定也不幹(畢竟是國內,你懂得)。

可見性問題

什麽是可見性?

可見性是指當多個消除訪問同一個變量時,一個線程修改了這個變量的值,其他線程能夠立即看到修改的值。

同樣舉個例子來說明一下:

public class RunThread extends Thread {

    private boolean isRunning = true;

    public boolean isRunning() {
        return isRunning;
    }

    public void setRunning(boolean isRunning) {
        this.isRunning = isRunning;
    }

    @Override
    public void run() {
        System.out.println(
"進入到run方法中了"); while (isRunning == true) { } System.out.println("線程執行完成了"); } } public class TestRun { public static void main(String[] args) { try { RunThread thread = new RunThread(); thread.start(); Thread.sleep(100); thread.setRunning(
false); } catch (Exception e) { e.printStackTrace(); } } }

我們會發現,RunThread線程無法正常結束了,進入了死循環了。這是由於在主線程中修改的isRunning值,並沒有及時對RunThread線程感知到,這就是可見性的問題,由於主線程修改了之後,RunThread線程並沒有立即能看到。

有序性問題
什麽是有序性?
有序性即程序執行的順序按照代碼的先後順序執行。

int a = 0;
int b = 0;
a = 1; //語句1
b = 2; //語句2
int c = a * a;//語句3 
int d = b + c;//語句4

從上述代碼看,語句1在語句2之前執行,但是對於JVM來說情況未必如此,這就是指令重排序。

volatile使用場景
當需要避免並發時發生意想不到的結果,必須要保證以上三條都滿足,否則極有可能得到你不想要的結果,我們常用的操作有synchronized和lock來保證這些情況,但是有些情況下,我們其實可以使用輕量級並發volatile關鍵字,
但是它並不能保證原子性操作,因此,volatile並不能替代synchronized。
關於volatile使用的場景:

  • 對變量的寫操作不依賴於當前值
  • 該變量沒有包含在具有其他變量的不變式中

常見的場景有:

  • 狀態標記變量(如上文提到的RunThread例子中可以對isRunning變量使用volatile關鍵字)
  • 單例中雙重檢查鎖,見(http://www.cnblogs.com/woniu4/p/8287484.html)

什麽是指令重排序呢?簡單多說,就是處理器為了提升程序的運行效率,可能對輸入的代碼進行優化,它不保證程序各個語句的執行先後順序同代碼中的順利一致,但保證最後的結果和順序執行是一直的。
由於語句4依賴於語句3,則兩者的順序是不會調整的。

volatile關鍵字解析(一)