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规则,只是探讨结果出现的缘由。
若是文章中出现对某处有错误理解的地方,欢迎你们指出。
参考文章: