public class TestMemoryBarrier {
boolean running = false;
boolean get() {
return running;
}
void doSetTrue() {
running = true;
}
public static void main(String[] args) throws InterruptedException {
TestMemoryBarrier instance = new TestMemoryBarrier();
new Thread(
() -> {
while (!instance.get()) {
}
System.out.println("Thread 1 finished.");
}).start();
Thread.sleep(100);
new Thread(
() -> {
instance.doSetTrue();
System.out.println("Thread 2 finished.");
}).start();
}
}
複製代碼
首先啓動第一個線程線程1去循環獲取bool變量running
的值(默認是false
),若是running
變爲true
後循環結束,線程終止。html
再起第二個線程線程2去修改running
的值爲true
,當修改完後輸出提示。bash
這裏結果輸出是:多線程
Thread 2 finished.
複製代碼
由於沒法獲知線程2對共享變量running
作出的修改, 而後線程1一直處在運行狀態。app
這裏簡單說明一下Java Mememory Model簡稱JMM:測試
在本例中線程2其實是修改了本身本地內存中的running
值, 可是並無刷新到主內存中,線程1也一直在讀本身本地內存中的值,並無去主內存中從新獲取。ui
爲了讓例子最終能輸出spa
Thread 1 finished
複製代碼
方法:很簡單, 直接設置變量running
爲volatile,以保證其在多線程環境中的內存可見性問題。線程
本文的重點是對插入的內存屏障進行測試,因此以上只是開頭。3d
添加一個類,包含一個volatile的變量並賦值。code
在get()
方法return前, new一個這個新增類的實例對象,完整代碼:
class VolatileClass {
private volatile int a = 1;
}
public class TestMemoryBarrier {
boolean running = false;
boolean get() {
VolatileClass aClass = new VolatileClass();
return running;
}
void doSetTrue() {
running = true;
}
public static void main(String[] args) throws InterruptedException {
TestMemoryBarrier instance = new TestMemoryBarrier();
new Thread(
() -> {
while (!instance.get()) {
}
System.out.println("Thread 1 finished.");
}).start();
Thread.sleep(100);
new Thread(
() -> {
instance.doSetTrue();
System.out.println("Thread 2 finished.");
}).start();
}
}
複製代碼
這裏一樣能使線程1結束。緣由在於新建實例的時候對volatile變量進行了讀寫操做
volatile插入的內存屏障指令以下圖:(這裏不拉出重排序相關話題)
總結:在進行volatile變量寫的時候,StoreStore確保前面線程2修改的普通變量已經被flush到主內存中了。
爲了套用Happen-Before規則,這裏直接在get()
和doSetTrue()
方法上加synchronized
也能保證可見性問題。但這裏假設只在get()
方法上加同步呢? 結果是線程1也能獲取最新值。
JMM關於synchronized的兩條規定:
若是隻是在doSetTrue()
方法上加鎖,線程1並不會獲取最新的值,緣由是:雖然線程2已經將修改過的變量刷新到主存了,可是get()
方法並不會去從主存讀取最新的值。
以上例子可能不符合happen-before規則,只是探討結果出現的緣由。
若是文章中出現對某處有錯誤理解的地方,歡迎你們指出。
參考文章: