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 后循环结束,线程终止。
再起第二个线程线程2去修改 running 的值为 true ,当修改完后输出提示。
这里结果输出是:
Thread 2 finished. 复制代码
因为无法获知线程2对共享变量 running 做出的修改, 然后线程1一直处在运行状态。
这里简单说明一下Java Mememory Model简称JMM:
在本例中线程2实际上是修改了自己本地内存中的 running 值, 但是并没有刷新到主内存中,线程1也一直在读自己本地内存中的值,并没有去主内存中重新获取。
为了让例子最终能输出
Thread 1 finished 复制代码
方法:很简单, 直接设置变量 running 为volatile,以保证其在多线程环境中的内存可见性问题。
本文的重点是对插入的内存屏障进行测试,所以以上只是开头。
添加一个类,包含一个volatile的变量并 赋值 。
在 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();
}
}
复制代码
volatile插入的内存屏障指令如下图:(这里不拉出重排序相关话题)
总结:在进行volatile变量写的时候,StoreStore确保前面线程2修改的普通变量已经被flush到主内存中了。
为了套用Happen-Before规则,这里直接在 get() 和 doSetTrue() 方法上加 synchronized 也能保证可见性问题。但这里假设只在 get() 方法上加同步呢? 结果是线程1也能获取最新值。
JMM关于synchronized的两条规定:
如果只是在 doSetTrue() 方法上加锁,线程1并不会获取最新的值,原因是:虽然线程2已经将修改过的变量刷新到主存了,但是 get() 方法并不会去从主存读取最新的值。