前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >记一次对Java多线程内存可见性的测试

记一次对Java多线程内存可见性的测试

作者头像
顾翔
发布2019-12-12 12:17:15
5430
发布2019-12-12 12:17:15
举报
文章被收录于专栏:啄木鸟软件测试

来源:http://www.51testing.com

 首先贴一段测试代码:

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,当修改完后输出提示。

注意: 第一个线程启动后主线程睡眠一段时间,确保第一个线程已经先于线程二获取cpu时间片。

  这里结果输出是:

Thread 2 finished.

  因为无法获知线程2对共享变量running做出的修改, 然后线程1一直处在运行状态。

  这里简单说明一下Java Mememory Model简称JMM:

  在本例中线程2实际上是修改了自己本地内存中的running值, 但是并没有刷新到主内存中,线程1也一直在读自己本地内存中的值,并没有去主内存中重新获取。

  为了让例子最终能输出

Thread 1 finished

  方法:很简单, 直接设置变量running为volatile,以保证其在多线程环境中的内存可见性问题。

  本文的重点是对插入的内存屏障进行测试,所以以上只是开头。

 测试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();  }

这里同样能使线程1结束。原因在于新建实例的时候对volatile变量进行了读写操作

  volatile插入的内存屏障指令如下图:(这里不拉出重排序相关话题)

  总结:在进行volatile变量写的时候,StoreStore确保前面线程2修改的普通变量已经被flush到主内存中了。

 测试synchronized关键字对可见性的影响:

  为了套用Happen-Before规则,这里直接在get()和doSetTrue()方法上加synchronized 也能保证可见性问题。但这里假设只在get()方法上加同步呢? 结果是线程1也能获取最新值。

  JMM关于synchronized的两条规定:

  线程解锁前,必须把共享变量的最新值刷新到主内存中

  线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值

  如果只是在doSetTrue()方法上加锁,线程1并不会获取最新的值,原因是:虽然线程2已经将修改过的变量刷新到主存了,但是get()方法并不会去从主存读取最新的值。

  以上例子可能不符合happen-before规则,只是探讨结果出现的原因。

星云测试

http://www.teststars.cc

奇林软件

http://www.kylinpet.com

联合通测

http://www.quicktesting.net

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-05-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档