volatile的使用方法及一些注意事项
volatile的定义
volatile是java虚拟机提供的最轻量级的同步机制, 当一个变量定义为volatile之后, 具备两个特性:
- 保证此变量对所有线程的可见性, “可见性” 指当一个线程改变了这个变量的值, 新值对于其他线程来说是立即可见的. 普通变量的值在线程之间传递必须通过主内存完成.
- 禁止指令重排序优化. 普通的变量只能保证该方法的执行过程中所依赖赋值结果的地方能获取正确的结果, 但不能保证赋值操作的执行顺序与程序代码顺序一致.
拓展: - 该变量修饰的变量并不保证原子性
- java中三个实现可见性的关键字: synchronized, final, volatile
- java中两个实现线程之间有序性关键字: volatile(禁止指令重排), synchronized(一个变量在同一时刻只允许一个线程对其进行lock)
使用场景
当多个线程依赖一个变量值做状态改变来执行时, 由于测试代码要求要求比较高, 这里只列举样例.
volatile boolean stop; //一个线程调用 public void shutdown(){ stop = true; } //多个线程执行doWork方法 public void doWork(){ while(!stop){ // do work } }
禁止指令重排优化, 标准的 双锁检测 单例. 当instance变量被 volatile 修饰以后, 会比没有 volatile 多执行一个内存屏障, 内存屏障能保证多个访问的一致性.
public class Singleton { private volatile static Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }
## 常见的使用问题
1. 上面说到, volatile 是保持多个线程一致性的. 但是并不是说被 volatile 修饰的变量具有原子性.
```java
public class VolatileDemo {
static volatile int race = 0;
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 10000; j++) {
race++;
}
}).start();
}
while (Thread.activeCount() > 1) {
Thread.yield();
}
System.out.println(race);
}
}
// output: 70254
如果需要解决上面的问题, 最简单粗暴的方式就是对 race++ 加锁.
总结
volatile 是一个在功能上弱化的 synchronized , 但是又有很多不同, 使用时一定要注意场景.
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 zhao4xi@126.com
文章标题:volatile的使用方法及一些注意事项
文章字数:558
本文作者:Zhaoxi
发布时间:2018-12-23, 15:04:16
最后更新:2019-09-21, 15:04:46
原始链接:http://zhao4xi.github.io/2018/12/23/volatile的使用方法及一些注意事项/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。