Java内存模型是通过各种操作来定义的,包括对变量的读/写操作,监视器的加锁和释放操作,以及线程的启动和合并操作。JMM为程序中所有的操作定义了一个偏序关系,称之为Happens-Before。要想保证执行操作B的线程看到操作A的结果(无论A和B是否在同一个线程中执行),那么A和B之间必须满足Happens-Before关系。如果两个操作之间缺乏Happens-Before关系,那么JVM可以对它们任意地重排序。

  当一个变量被多个线程读取并且至少被一个线程写入时,如果在读操作和写操作之间没有按照Happens-Before来排序,那么就会产生数据竞争问题。在正确同步的程序中不存在数据竞争,并会表现出串行一致性,这意味着程序中的所有操作都会按照一种固定的和全局的顺序执行。

Happens-Before规则:

  • 程序顺序规则:如果程序中操作A在操作B之前,那么线程中A操作将在B操作之前执行。
  • 监视器锁规则:对一个锁的解锁操作必须在随后对这个锁的加锁操作之前执行。
  • volatile变量规则:对volatile变量的写入操作必须在对该变量的读操作之前执行。
  • 线程启动规则:在线程上对Tread.start()的调用必须在该线程中执行任何操作之前执行。
  • 线程结束规则:线程中的任何操作都必须在其他线程中检测到该线程已经结束之前执行,或者从Thread.join()中成功返回,或者在调用Thread.isAlive()时返回false。
      • 如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作在线程A从ThreadB.join()操作成功返回之前执行。
  • 中断规则:当一个线程在另一个线程上调用interrupt()时,必须在被中断线程检测到interrupt()调用之前执行。(通过抛出InterruptException,或者调用isInterrupted和interrupted)
  • 终结器规则:对象的构造函数必须在启动该对象的终结器(finalize())之前执行完成。
  • 传递性:如果操作A在操作B之前执行,并且操作B在操作C之前执行,那么操作A必须在操作C之前执行。

  两个线程在使用同一个锁进行同步时,在它们之间的Happens-Before关系:在县城A内部的所有操作都按照它们在源程序中的先后顺序来排序,在线程B内部的操作也是如此。由于A释放了锁M,并且B随后获取了锁M,因此A中所有在释放锁之前的操作,也就位于B中请求锁之后的所有操作之前。

  如果这两个线程是在不同的锁上进行同步的,那么就不能推断出他们之间的动作顺序,因为在这两个线程的操作之间并不存在Happens-Before关系。

最新文章

  1. Servlet和CGI的区别
  2. Using GET_APPLICATION_PROPERTY in Oracle D2k Forms
  3. 【转】kylin优化
  4. wdcp升级php版本到5.3,5.5
  5. HTML常见元素集锦
  6. 对日期和时间的处理 NSCalendar
  7. Oracle12c功能增强 新特性之管理功能的增强
  8. Android 巧妙实现图片和文字上下布局或者左右布局
  9. Python学习笔记整理(三)Python中的动态类型简介
  10. 【剑指offer】不用加减乘除做加法
  11. 【BZOJ4237】稻草人(CDQ分治,单调栈)
  12. 用 Freemarker 生成 word 文档
  13. [原创]K8 cping 3.0大型内网渗透扫描工具
  14. Servlet案例2:文件下载
  15. django用mysql数据库出现的问题解决
  16. ubuntu源列表(清华,阿里,官方,选一即可)
  17. classpath路劲
  18. Linux命令: 在线练习网址
  19. 教你Snapseed软件八个常用调图工具
  20. CodeForces 42C Safe cracking 规律题

热门文章

  1. WPF之数据绑定Data Binding
  2. Istio 1.6架构及性能
  3. [Android应用开发] 03.网络编程
  4. Java实现 LeetCode 705 设计哈希集合(使用数组保存有没有被用过)
  5. Java实现 LeetCode 113 路径总和 II
  6. Java实现蓝桥杯G将军
  7. 什么是 JVM ?
  8. java实现手机尾号评分
  9. java实现填写算式
  10. docker-compose mysql和node连接认证mongo问题