簡單學:併發編程之volatile

關於java併發編程的相關文章都是閱讀了《java併發編程實戰》以後的讀書筆記總結,另外本文還參考和引用了Java 理論與實踐: 正確使用 Volatile 變量html

在java的鎖機制中(例如synchronized),主要包含了兩種特性,即原子性(互斥)和可見性。原子性即一次只容許一個線程可以持有某個特定的鎖,並訪問其代碼塊。所以原子性能夠用於實現對共享數據對協調訪問,一次只有一個線程能夠訪問其共享對象。java

volatile保證可見性

java中的volatile關鍵字能夠被認爲是一種輕量級的synchronized機制,訪問volatile變量的時候並不會執行加鎖操做,不會致使線程阻塞,同時它又和鎖機制同樣,都具有可見性的特性(可是並不具有原子特性)。當把變量生命爲volatile類型後,編譯器與運行時都會注意到這個變量是共享的,所以不會把這個變量上的操做與其餘內存操做一塊兒重排序。volatile變量不會被緩存在寄存器或者其餘處理器不可見的地方。所以在讀取volatile變量的時候老是會返回最新寫入的值。編程

volatile提供線程安全

volatile變量能夠用於提供線程安全,可是必須同時知足如下兩個條件:緩存

  • 對變量的寫操做不依賴於當前的值安全

  • 該變量沒有包含在具備其餘變量的不變式之中併發

正確使用volatile關鍵字

在使用volatile關鍵字的時候,要始終牢記一個原則--只有在狀態真正獨立於程序內其餘內容時才能使用 volatilethis

  • 狀態標識:spa

    volatile變量能夠做爲一個布爾狀態標識,用於指示發生一次重要性時間,例如中止線程等線程

class Test {
    static volatile boolean isRunning;

    private static class TestThread extends Thread {
        public void run() {
            while (!isRunning) {
                System.out.println("the thread is still running");
            }
        }
    }

    public static void main(String[] args) {
        new TestThread().start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        isRunning = true;
    }
}複製代碼

在上面的例子中,咱們在isRunning這個布爾變量前面加上了volatile進行修飾,以確保在main線程當中對它的修改可以被咱們開啓的子線程所看到。(若是咱們沒有添加volatile關鍵字,有可能子線程會持續運行而看不到isRunning的值的改變)code

  • 一次性安全發佈:

    在缺少同步的狀況下,可能會遇到某個對象引用的更新值(由另外一個線程寫入)和該對象狀態的舊值同時存在。經過使用volatile關鍵字,咱們能夠實現安全發佈對象。

public class BackgroundFloobleLoader {
    public volatile Flooble theFlooble;

    public void initInBackground() {
        theFlooble = new Flooble();  // this is the only write to theFlooble
    }
}

public class SomeOtherClass {
    public void doWork() {
        while (true) {
            // use the Flooble, but only if it is ready
            if (floobleLoader.theFlooble != null)
                doSomething(floobleLoader.theFlooble);
        }
    }
}複製代碼

⚠️上面的安全發佈的必要條件是:被髮布的對象必須是線程安全的,或者是有效的不可變對象(有效不可變意味着對象的狀態在發佈以後永遠不會被修改)。volatile 類型的引用能夠確保對象的發佈形式的可見性,可是若是對象的狀態在發佈後將發生更改,那麼就須要額外的同步。

  • 獨立觀察:

    按期發佈觀察結果供程序內部使用。以下面的例子所示:

public class UserManager {
    public volatile String lastUser;

    public boolean authenticate(String user, String password) {
        boolean valid = passwordIsValid(user, password);
        if (valid) {
            User u = new User();
            activeUsers.add(u);
            lastUser = user;
        }
        return valid;
    }
}複製代碼

⚠️上面這種用例要求被髮布的值是有效不可變的 —— 即值的狀態在發佈後不會更改

  • 開銷較低的讀-寫鎖策略:

    若是對於某個變量的值的讀操做遠遠超過寫操做,咱們能夠經過將內置鎖(幫助咱們實現操做的原子性)和volatile關鍵字相結合,實現開銷較低的讀-寫鎖.

@ThreadSafe
public class CheesyCounter {
    // Employs the cheap read-write lock trick
    // All mutative operations MUST be done with the 'this' lock held
    @GuardedBy("this");
    private volatile int value;

    public int getValue() {
        return value;

    public synchronized int increment() {
        return value++;
    }
}複製代碼
相關文章
相關標籤/搜索