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、對于阻塞隊列,消費者一定能看到生産者所有的操作結果。