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. spark完整的数据倾斜解决方案

    1.数据倾斜的原理 2.数据倾斜的现象 3.数据倾斜的产生原因与定位 在执行shuffle操作的时候,大家都知道,我们之前讲解过shuffle的原理. 是按照key,来进行values的数据的输出.拉 ...

  2. Guess Number Higher or Lower II -- LeetCode

    We are playing the Guess Game. The game is as follows: I pick a number from 1 to n. You have to gues ...

  3. Codeforces Round #535 (Div. 3) [codeforces div3 难度测评]

    hhhh感觉我真的太久没有接触过OI了 大约是前天听到JK他们约着一起刷codeforces,假期里觉得有些颓废的我忽然也心血来潮来看看题目 今天看codeforces才知道居然有div3了,感觉应该 ...

  4. OC语言基础之代码的封装

    1.封装的注意点 1: // 成员变量尽量不要用@public 2: // @public 3: int age; 1: //@public 2: // 只读(readonly):只允许外界访问我的n ...

  5. CSS3:2D转换方法

    利用transform 可以实现旋转.缩放.倾斜.移动 属性有:translate.scale 移动: translateX(10px); //水平方向移动10px translateY(50px); ...

  6. DOM系统学习-基础

    DOM介绍  DOM介绍: D 网页文档 O 对象,可以调用属性和方法 M 网页文档的树型结构  节点: DOM将树型结构理解为由节点组成.     节点种类: 元素节点.文本节点.属性节点等 查找元 ...

  7. Windows环境下,用netstat命令查看某个端口号是否占用

    目标:在Windows环境下,用netstat命令查看某个端口号是否占用,为哪个进程所占用. 操作:操作分为两步:(1)查看该端口被那个PID所占用;方法一:有针对性的查看端口,使用命令 Netsta ...

  8. C++ 如何得到当前进程所占用的内存呢?【转】

    使用SDK的PSAPI (Process Status Helper)中的BOOL GetProcessMemoryInfo( HANDLE Process, PPROCESS_MEMORY_COUN ...

  9. 通过java api提交自定义hadoop 作业

    通过API操作之前要先了解几个基本知识 一.hadoop的基本数据类型和java的基本数据类型是不一样的,但是都存在对应的关系 如下图 如果需要定义自己的数据类型,则必须实现Writable hado ...

  10. 使用Zxing 一维码

    最近看到满大街的二维码扫码有惊喜,对二维码也有过一些了解,想看看到底是什么原理,在网上找了一些资料,自己弄了一个实例,采用的是MVC,贴出来分享一下 一维码生成 Controller public A ...