volatile
volatile是Java中一种比sychronized关键字更轻量级的较弱的同步机制。与sychronized相比,在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞。volatile 变量所需的编码较少,并且运行时开销也较少。
volatile变量具有的特性:
1、可见性:使用volatile可以保证某一个线程对变量的更新对于其他所有线程是可见的。
2、有序性:禁止指令重排序优化,在一定程度上保证了有序性。
3、原子性:volatile没办法保证对变量的操作的原子性。(synchronized和Lock可以)
volatile修饰变量var保证可见性的三个步骤:
1、使用volatile关键字会强制将修改的值立即写入主存。
2、当线程2进行修改时,会导致线程1的工作内存中缓存变量var的缓存行无效.
3、由于线程1的工作内存中缓存变量var的缓存行无效,所以线程1再次读取变量var的值时会去主存读取。
也就是说,每次读取volatile修饰的变量都需要从主存中读取,而不是缓存中。
禁止指令重排序:
1、当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行。
2、在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。
使用场景:
1、对变量的写操作不依赖于当前值。
比如:volatile 变量不能用作线程安全计数器。虽然增量操作(x++
)看上去类似一个单独操作,实际上它是一个由(读取-修改-写入)操作序列组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。实现正确的操作需要使x
的值在操作期间保持不变,而 volatile 变量无法实现这点。
2、该变量没有包含在具有其他变量的不变式中。(保证原子性)
例子:下面是一个非线程安全的数值范围类。它包含了一个不变式 —— 下界总是小于或等于上界。
public class NumberRange {
private volatile int lower;
private volatile int upper;
public int getLower() { return lower; }
public int getUpper() { return upper; }
public void setLower(int value) {
if (value > upper)
throw new IllegalArgumentException(...);
lower = value;
}
public void setUpper(int value) {
if (value < lower)
throw new IllegalArgumentException(...);
upper = value;
}
}
将 lower
和 upper 字段定义为 volatile 类型不能够充分实现类的线程安全;而仍然需要使用同步——使 setLower()
和 setUpper()
操作原子化。
否则,如果凑巧两个线程在同一时间使用不一致的值执行 setLower
和 setUpper
的话,则会使范围处于不一致的状态。例如,如果初始状态是(0, 5)
,同一时间内,线程 A 调用setLower(4)
并且线程 B 调用setUpper(3)
,显然这两个操作交叉存入的值是不符合条件的,那么两个线程都会通过用于保护不变式的检查,使得最后的范围值是(4, 3)
—— 一个无效值。
这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。
参考链接:http://www.importnew.com/18126.html
https://www.cnblogs.com/ouyxy/p/7242563.html