關於java併發編程的相關文章都是閱讀了《java併發編程實戰》以後的讀書筆記總結,另外本文還參考和引用了Java 理論與實踐: 正確使用 Volatile 變量html
在java的鎖機制中(例如synchronized),主要包含了兩種特性,即原子性(互斥)和可見性。原子性即一次只容許一個線程可以持有某個特定的鎖,並訪問其代碼塊。所以原子性能夠用於實現對共享數據對協調訪問,一次只有一個線程能夠訪問其共享對象。java
java中的volatile關鍵字能夠被認爲是一種輕量級的synchronized機制,訪問volatile變量的時候並不會執行加鎖操做,不會致使線程阻塞,同時它又和鎖機制同樣,都具有可見性的特性(可是並不具有原子特性)。當把變量生命爲volatile類型後,編譯器與運行時都會注意到這個變量是共享的,所以不會把這個變量上的操做與其餘內存操做一塊兒重排序。volatile變量不會被緩存在寄存器或者其餘處理器不可見的地方。所以在讀取volatile變量的時候老是會返回最新寫入的值。編程
volatile變量能夠用於提供線程安全,可是必須同時知足如下兩個條件:緩存
對變量的寫操做不依賴於當前的值安全
該變量沒有包含在具備其餘變量的不變式之中併發
在使用volatile關鍵字的時候,要始終牢記一個原則--只有在狀態真正獨立於程序內其餘內容時才能使用 volatile。this
狀態標識: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++;
}
}複製代碼