volatile 的使用
Posted youxin2012
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了volatile 的使用相关的知识,希望对你有一定的参考价值。
volatile, java中最轻量级的同步机制。虽然常用于线程同步中,然而并不意味着volatile修饰的变量操作时具有原子性,请注意“volatile修饰的变量”操作时真的不具备原子性,实例如下:
public class VolatileTest
public static volatile int race = 0;
public static void increase()
race++;
private static final int THREADS_COUNT = 20;
public static void main(String[] args)
Thread[] threads = new Thread[THREADS_COUNT];
for (int i = 0; i < THREADS_COUNT; i++)
threads[i] = new Thread(new Runnable()
@Override
public void run()
for (int i = 0; i < 10000; i++)
increase();
);
threads[i].start();
// 等待所有累加线程都结束
while (Thread.activeCount() > 1)
Thread.yield();
System.out.println(race);
多次结果发现小于 20*10000,证明 volatile修饰的race在做 自增运算时,并非原子的,race++ 操作对应的机器指令依然被分解为: 1. 读取race 2. race+1 3. 向race对应的地址空间赋值 。由于1,2,3没有同步保证,所以步骤2中 race的值可能已被其他线程更改,所以整个过程非原子操作。 通过 javap -v xxx.class 对产生的class文件做反编译,查看对应的字节码可以看到race++操作被分解为多个步骤:
明确了volatile不具备原子性的同时,我们要明确 volatile具有哪些特性:1. 可见行 2. 禁止指令排序优化
可见行
概念:当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。
补脑:在java内存模型中线程共享变量的读写操作在线程完成后均要同步会主内存。即当线程发生多操作时,需从主线程读取变量值并刷新线程中的值(线程中缓存该变量),线程中完成变量写操作后同样需同步回主内存。
然而volatile变量较其它变量的区别在于 volatile可以保证修改后的新值能立即同步到主内存,使用前立即从主内存刷新,可以保证多线程操作时变量的可见行。
典型应用:仅有一个线程对volatile修饰的变量做修改,其他线程读取监控时,可以及时将volatile状态的变化,反映到监控线程中。禁止指令排序优化
指令排序优化:jvm中的编译器在做编译时,常常会给予性能的考虑对 程序编码顺序对应的指令顺序做调整(需满足编译器认知范围内的结果一致性)以提高执行效率。
然而有时指令排序优化会改变程序的原意,导致意想不到的错误。
实例:volatile boolean initialized = false; //thread A config(); initialized = true; //thread B while(!initialize) sleep(); doSomethingWithConfig();
如上,当对线程A做指令排序调整后 可能出现
initalized = true;
config();
的顺序,此时 线程B在 感知到initalized = true后,线程A完成config()操作之前 执行了doSomethingWithConfig()时,常常会导致错误,而且较难排查,所以此处应将initialized声明为 volatile避免 指令排序优化造成错误。
以上总结参考 深入理解jvm
以上是关于volatile 的使用的主要内容,如果未能解决你的问题,请参考以下文章