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

 

java多线程之happens-before的更多相关文章

  1. Java多线程之ConcurrentSkipListMap深入分析(转)

    Java多线程之ConcurrentSkipListMap深入分析   一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下, ...

  2. JAVA多线程之wait/notify

    本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait ...

  3. JAVA多线程之volatile 与 synchronized 的比较

    一,volatile关键字的可见性 要想理解volatile关键字,得先了解下JAVA的内存模型,Java内存模型的抽象示意图如下: 从图中可以看出: ①每个线程都有一个自己的本地内存空间--线程栈空 ...

  4. java多线程之yield,join,wait,sleep的区别

    Java多线程之yield,join,wait,sleep的区别 Java多线程中,经常会遇到yield,join,wait和sleep方法.容易混淆他们的功能及作用.自己仔细研究了下,他们主要的区别 ...

  5. Java多线程之Runnable与Thread

    Java多线程之Thread与Runnable 一.Thread VS Runnable 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类和 ...

  6. JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止

    JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止 背景 当单线程的程序发生一个未捕获的异常时我们可以采用try....catch进行异常的捕获,但是在多线程环境 ...

  7. java多线程之wait和notify协作,生产者和消费者

    这篇直接贴代码了 package cn.javaBase.study_thread1; class Source { public static int num = 0; //假设这是馒头的数量 } ...

  8. Java——多线程之Lock锁

    Java多线系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多线 ...

  9. java多线程之:深入JVM锁机制2-Lock (转载)

    前文(深入JVM锁机制-synchronized)分析了JVM中的synchronized实现,本文继续分析JVM中的另一种锁Lock的实现.与synchronized不同的是,Lock完全用Java ...

  10. Java多线程之Callable接口与Runnable的实现以及选择

    通过实现Runnable接口的实现 package Thread; import java.util.concurrent.ExecutorService;import java.util.concu ...

随机推荐

  1. 新疆大学ACM-ICPC程序设计竞赛五月月赛(同步赛)- XOR(二进制使用)

    链接:https://www.nowcoder.com/acm/contest/116/H来源:牛客网 题目描述 Once there was a king called XOR, he had a ...

  2. Manacher【p1659】 [国家集训队]拉拉队排练

    题目描述 n个女生举牌子(只含有26个小写字母,长度为n的字符串), 如果连续的一段女生,有奇数个,并且她们手中的牌子所写的字母,从左到右和从右到左读起来一样,那么这一段女生就被称作和谐小群体. 现在 ...

  3. Cookie和Session在Node.JS中的实践(二)

    Cookie和Session在Node.JS中的实践(二) cookie篇在作者的上一篇文章Cookie和Session在Node.JS中的实践(一)已经是写得算是比较详细了,有兴趣可以翻看,这篇是s ...

  4. 【noip2017 day2T2】【蚯蚓】巧用队列单调性线性处理

    (画师当然是武内崇啦) Description 本题中,我们将用符号[c]表示对c向下取整,例如:[3.0」= [3.1」=[3.9」=3.蛐蛐国最近蚯蚓成灾了!隔壁跳蚤国的跳蚤也拿蚯蚓们没办法,蛐蛐 ...

  5. SQL 增删改查 复习

    首先创建两张表 CREATE TABLE Teacher ( Id ,) NOT NULL PRIMARY KEY, Name ) NOT NULL, ); CREATE TABLE Student ...

  6. 七. 多线程编程2.Java线程模型

    Java运行系统在很多方面依赖于线程,所有的类库设计都考虑到多线程.实际上,Java使用线程来使整个环境异步.这有利于通过防止CPU循环的浪费来减少无效部分. 为更好的理解多线程环境的优势可以将它与它 ...

  7. jtds链接SqlServer数据库(整合)

    先前使用的时候没做汇总,现在结合遇到的问题总结下. 开始使用jdbc驱动配置调用SqlServer不合适,根据网上的资料修改成了jtds配置方式. 当时使用的maven配置,配置如下: <spa ...

  8. cocurrent包 锁 Lock

    20. 锁 Lock java.util.concurrent.locks.Lock 是一个类似于 synchronized 块的线程同步机制.但是 Lock 比 synchronized 块更加灵活 ...

  9. 模拟Spring中的getBean方法

    一直知道Spring是运用反射技术的,但具体怎么用呢?今天就模拟下getBean方法. 步骤: 1.用Dom4j解析xml配置文件,取出我们需要的信息 2.遍历Bean节点,根据每个Bean节点的cl ...

  10. hdu1013(C++)

    9的余数定理:一个数各位数字的总和除以9的余数与它本身除以9的余数同等 大数问题:防止大数,用字符串来存入数据,再转化为数字 #include<iostream>#include<s ...