深入理解volatile关键字-(volatile有什么作用?)

volatile - 被其修饰的变量所具有的特性

1、保证该变量对所有线程的可见性
2、禁止指令重排序优化。

1. 保证该变量对所有线程的可见性:

可见性是指,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,同时其他线程每次使用前都会从主内存读取,从而新值对于其他线程来说是可以立即得知的。但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存来完成。

在 Java 内存模型(深入理解Java虚拟机之----Java内存模型)中,可以知道,在多线程中,每一个线程都有各自的工作内存,工作内存是该线程使用到的变量的主内存副本拷贝,对变量的所有操作(读取、赋值等)都必须在工作内存中进行,并且不会立即更新至主内存中。这就可能造成一个变量在一个线程中修改了,还没来得及更新至主内存,主内存中该变量的值就被其他线程使用了,而此时变量的值为修改前的旧值。

volatile 变量不一样,它拥有特殊的访问规则:

(1)使用前必须先从主内存刷新最新的值;

(2)修改后必须立刻同步回主内存中。

以上两条特殊规则,给人以 volatile 变量不是存在于工作内存而是存在于主内存中 的假象,从而保证了 volatile 变量对所有线程的可见性。

虽然 volatile 变量对所有线程是立即可见的,但是,基于 volatile 变量的运算在并发下不一定是安全的。

volatile 关键字能够保证 a)操作读取的 race 的值在这一时刻是正确的,但在执行 b)、c)操作时,可能有其他的线程已经对 race 的值进行了修改,导致了 c)操作可能把较小的 race 值同步回主内存之中。所以要想保证结果的正确性,需要在 increase() 方法加锁才行(原子性)。

以下是适用 volatile 变量的两种运算场景:

运算结果并不依赖变量的当前值,或者能够保证确保只有单一的线程修改变量的值。

变量不需要与其他的状态变量共同参与不变约束。

2. 禁止指令重排序优化。

为了尽可能的减小内存操作速度远慢于 CPU 运行速度所带来的 CPU 空置的影响,虚拟机会按照自己的一些规则将程序编写顺序打乱,而这一打乱,就可能干扰程序的并发执行

对应于 double-check locking 例子来说就是:

instance = new Singleton(); 这一行代码分三个步骤:

(1)在堆上分配内存;
(2)赋初值;
(3)将 instance 引用指向堆地址。

假如是以(1)(3)(2)的顺序执行,其他线程就可能得到一个未完成初始化的 instance ,导致程序报错。而添加 volatile 修饰之后可以阻止指令的重排序(Java 1.5 及以后的版本),从而避免了这种情况。

volatile 关键字是 Java 虚拟机提供的最轻量级的同步机制,使用volatile可能比锁更快,但在某些情况下它不起作用。在 Java 5 中扩展了 volatile 有效的情况范围。 特别是,双重检查加锁机制现在可以正常工作。

你可能感兴趣的:(JVM,JVM原理,volatile,jvm)