1、背景问题

在讲happens-before之前,先引入一个例子:

假定我们有已经被初始化的变量:

int counter = 0;
这个 counter 变量被两个线程所共有,也就是说线程A和线程B都可以获取或者更改counter的值。 这里我们假设线程A要增加counter的值:
counter++;

然后,线程B打印counter的值
System.out.println(counter);

如果上面两条语句被同一个线程执行,我们可以肯定的说打印出来的值是1. 但是如果这两条语句分别被两个线程执行,其打印出来的值却可能是0(如果不知道为何为0,请参考我的另一篇文章java多线程之内存可见性),
因为这里并没有任何保证说线程A对counter的修改一定对线程B所见。除非我们在两条语句之间建立起 happens-before的关系。

2、happens-before关系

什么是happens-before关系? 这个关系其实就是一个保证而已,那么保证什么呢?它保证一条语句对内存的写操作对另一条语句是可见的。换句话说,如果写操作A和读操作B存在happens-before这种关系,那么写操作在结束以后都操作才能开始。

下面是Java内存模型中的八条可保证happen—before的规则,它们无需任何同步器协助就已经存在,可以在编码中直接使用。
1、程序次序规则:在一个单独的线程中,按照程序代码的执行流顺序,(时间上)先执行的操作happen—before(时间上)后执行的操作。
2、管理锁定规则:一个unlock操作happen—before后面(时间上的先后顺序,下同)对同一个锁的lock操作。
3、volatile变量规则:对一个volatile变量的写操作happen—before后面对该变量的读操作。
4、线程启动规则:Thread对象的start()方法happen—before此线程的每一个动作。
5、线程终止规则:线程的所有操作都happen—before对此线程的终止检测,可以通过Thread.join()方法结束、Thread.isAlive()的返回值等手段检测到线程已经终止执行。
6、线程中断规则:对线程interrupt()方法的调用happen—before发生于被中断线程的代码检测到中断时事件的发生。
7、对象终结规则:一个对象的初始化完成(构造函数执行结束)happen—before它的finalize()方法的开始。
8、传递性:如果操作A happen—before操作B,操作B happen—before操作C,那么可以得出A happen—before操作C。

这里的八个规则除了第三个以外都容易理解。所以专门讲一下volatile变量规则。

1、 对volatile变量执行写操作时,会在写操作后加入一条store屏障指令

2、 对volatile变量执行读操作时,会在读操作前加入一条load屏障指令。

通俗得讲,volatile变量在每次被线程访问时,都强迫从主内存中读该变量的值,而当该变量发生变化时,又会强迫将最新的值刷新到主内存。这样任何时刻,不同的线程总是能够看到该变量的最新值。

如果你不能够理解,我们可以采取一种极端的思维方式:如果有两个线程的话,对于一个普通变量,在java内存模型中它是有三个拷贝的,一个在主内存,另外两个在线程的工作内存里。如果刷新不及时,那么就可能导致两个工作内存中的变量值不一致。但是对于volatile变量,你完全可以假定其只有一份而且唯一一份拷贝在主内存中发生,所以当两个线程想对volatile变量进行更改或者读取的时候,总是得等其中一个线程完成以后才行。

参考:

https://docs.oracle.com/javase/tutorial/essential/concurrency/memconsist.html

https://en.wikipedia.org/wiki/Happened-before

http://blog.csdn.net/ns_code/article/details/17348313

https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.5

 

最新文章

  1. mysql忘记root密码怎么办?
  2. .NET中使用NLog记录日志
  3. C++11引用临时变量的终极解析
  4. 报表引擎API开发入门—简单程序数据集
  5. iOS开发UI篇-懒加载、重写setter方法赋值
  6. vj1010:高精乘+细心模拟
  7. 在MFC对话框中添加状态栏
  8. PL/SQL编程重点语句输出整理
  9. javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
  10. Final互评------《飞词》---- 拉格朗日2018
  11. C#实现发布订阅模式
  12. 面试求职中你需要了解的Java面向对象
  13. PHP DES加解密
  14. loadrunner脚本编写经验
  15. thinkphp5.0返回插入数据id
  16. [PY3]——过滤数据——列表推导、filter()、itertools.compress()
  17. sort 、sorted、range、join方法 数字的正序、倒叙、翻转
  18. Java 并发:内置锁 Synchronized
  19. 页面置换算 - FIFO、LFU、LRU
  20. Linux 常用资源

热门文章

  1. CV2
  2. Spcomm 属性详解
  3. shell中的cut命令
  4. vsftp 服务配置篇
  5. DBS:同学录
  6. IE常见BUG总结(持续更新)
  7. Spring Bean Life Cycle Methods – InitializingBean, DisposableBean, @PostConstruct, @PreDestroy and *Aware interfaces
  8. CSU - 1542 Flipping Parentheses (线段树)
  9. CKEditor+SWFUpload实现功能较为强大的编辑器(二)---SWFUpload配置
  10. zabbix监控php-fpm