Happens-Before和Volatile关键字解析

2019-10-27  本文已影响0人  斯文遮阳

Java内存模型

Java虚拟机中试图定义一种Java内存模型(Java Memory Model,JMM)来屏蔽各种硬件和操作系统的内存访问差异,以实现让Java程序在各自平台下都能达到一致的内存访问效果。

Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。此处的变量包括实例字段、静态字段和构成数组对象的元素,但不包括局部变量与方法参数,因为后者是线程私有的,不会被共享。

如下图,Java内存模型中所有的变量都存储在主内存中(可以类比为PC的内存),每个线程还有自己的工作内存(可以类比为PC的处理器与高速缓存)。线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。

Java内存模型

Java内存模型定义了以下8中操作来实现主内存和工作内存之间的交互,其中每一个操作都是原子的、不可再分的(对于double和long类型的变量来说,load、store、read和write操作允许有例外)。

注意,Java内存模型要求read和load操作、store和write操作必须顺序执行,而不是连续执行。

Happens-before(先行发生)原则

如果Java内存模型中所有的有序性都仅仅靠volatile和synchronized来完成,那么有一些操作将变得十分繁琐。但是我们在写Java并发代码时并没有感觉到这一点,这是因为Java语言中有一个“Happens-before”原则。这个原则非常重要,它是判断数据是否存在竞争、线程是否安全的主要依据,依靠这个原则我们可以通过几条规则判断并发环境下两个操作之间是否可能存在冲突。

“Happens-before”指的是Java内存模型中定义的两项操作之间的偏序关系,如果说操作A先行发生于操作B,其实就是说发生操作B之前,操作A产生的影响能被操作B观察到,“影响”包括修改了内存中共享变量的值、发送了消息、掉用了方法等。下面是Java内存模型中“天然的”先行发生关系,如果两个操作之间的关系不在此列,并且无法通过下列规则推导出来的话,它们就没有顺序性保障,虚拟机可以对它们随意地进行重排序。

Volatile关键字解析

关键字volatile是Java虚拟机提供的最轻量级的同步机制。当一个变量定义为volatile之后,它将具备两种特性:可见性和禁止指令重排序优化。

可见性是指此变量对所有线程的可见性,即当一个线程修改了变量值之后,新值对于其他线程来说是可以立即得知的(普通变量需要回写到主内存中、另一个线程再从主内存读取才行)。但这并不意味着volatile变量的运算一定是线程安全的,因为Java里的运算并非是原子操作。

// 此处应有代码
test(){}

由于volatile变量只能保证可见性,在不符合以下两条规则的运算场景中,我们仍要通过加锁来保证原子性。volatile最常用的一个场景是作为Bool值进行状态判定。

Volatile关键字的性能怎么样?volatile变量读操作的性能消耗与普通变量几乎没有什么差别,但是写操作则会慢一些,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。不过即使如此,大多数场景volatile的总开销比锁要低,在volatile和锁之中选择的唯一依据仅仅是volatile的语义能否满足你的需求。

上一篇下一篇

猜你喜欢

热点阅读