天天看點

Happens-Before原則1、什麼是Happens-Before?2、Happens-Before規則有哪些3、線程安全工具類的Happens-Before原則

1、什麼是Happens-Before?

簡單來說,A操作Happens-Before B操作,那A操作的結果就對B操作可見。Happens-Before主要來結果可見性的。

2、Happens-Before規則有哪些

2.1、程式順序規則(單線程)

在一個線程中,如果B操作需要A操作的結果,那A操作的結果肯定對B可見。如下代碼,由于執行重排序,A操作和C操作可能不是按順序進行,但B操作一定在A操作和C操作之後執行。可以稱作A操作Happens-Before B操作

int a = 0;
int b = a + 1;    // A操作
int c = a + 2;    // C操作
int d = b + c;    // B操作
           

2.2、鎖規則

對于同一個鎖N,如果兩個線程A、B同時競争鎖N,如果A先得到鎖,那麼A線程操作的結果将對B線程可見,可以稱作A操作Happens-Before B操作。

// 線程A
synchronized ("a") {
    a = 1;
}
           
// 線程B
synchronized ("a") {
     b = a + 1;
}
           

2.3、volatile

隻要保證線程A完成了寫操作,線程B就一定能看到,線程A寫操作之前的所有非volatile操作對B也都是可見的,可以實作輕量級的同步。variable禁止重排序。如下代碼,如果線程A對b完成了寫操作,線程B在讀b時就會看到線程A對a和b的操作結果。

int a = 0;
volatile int b = 0;

//線程A    
public void volatileTestA() {
    a = 1;
    b = 1;
}
           
// 線程B
public void volatileTestB() {
    int c = a + b;
}
           

2.4、線程啟動

子線程所執行的所有語句都能看到主線程調用start()之前的所有語句的發生結果。如下代碼,線程B執行體可以看到a、b的操作結果

Thread threadB = new Thread(() -> {
    c = a + b;
}, "線程B");
a = 1;
b = 2;
threadB.start();
           

2.5、線程join

join方法之後的所有語句都能看到線程B中所有語句的發生結果。如下代碼,這裡在執行c操作時,可以看到線程B對a和b的操作結果。

Thread threadB = new Thread(() -> {    
    a = 1;
    b = 2;
}, "線程B").start();
threadB.join();
c = a + b;
           

2.6、傳遞性

如果hb(A,B)而且hb(B,C),那麼hb(A,C)。這個很好了解,如果B操作可以看到A操作的結果,C操作可以看到B操作的結果,那麼C操作肯定能看到A操作的結果。

2.7、線程中斷

一個線程B被其他線程interrupt時,那麼檢測中斷(threadB.isInterrupted()或Thread.interrupted())一定為true或者抛出InterruptedException。

Thread threadB = new Thread(() -> {
      Thread.interrupted();
}, "線程B");
threadB.start();
threadB.interrupt();
           

2.8、對象終結

對象構造方法的最後一行指令happens-before于finalize()方法的第一行指令。finalize()在對象被垃圾收集器析構(回收)之前調用,方法體是在對象銷毀時候要執行的操作

public ClassEndDemo1() {
    a = 1;
}

@Override
protected void finalize() {
    int b = a + 1;
}
           

3、線程安全工具類的Happens-Before原則

一下線程安全工具類所具有的Happens-Before原則,也是依賴于上面的8個原則。  

1、線程安全的容器get操作一定能看到在此之前的put等寫入操作的結果;

2、CountDownLatch、CyclicBarrier,await方法後的操作能看到之前的操作的結果;

3、Semaphore,新獲得許可證的線程能看到釋放許可證線程的所有操作結果;

4、Future、FutureTask,get方法後的操作可以看到之前的操作結果;

5、線程池,給線程池每一個線程都可以看到在execute之前的執行結果;

6、對于阻塞隊列,消費者一定能看到生産者所有的操作結果。