volatile变量
在Java语言中,volatile变量提供了一种轻量级的同步机制,volatile变量用来确保将变量的更新操作通知到其它线程,volatile变量不会被缓存到寄存器或者对其它处理器不可见的地方,所以在读取volatile变量时总会返回最新写入的值,volatile变量通常用来表示某个状态标识。
原子变量:
原子变量是“更强大的volatile”变量,从实现来看,每个原子变量类的value属性都是一个volatile变量,所以volatile变量的特性原子变量也有。同时,原子变量提供读、改、写的原子操作,更强大,更符合一般并发场景的需求。
既然原子变量更强大,是否还有必要使用volatile变量?如果有什么时候选择volatile变量,什么时候选择原子变量?当然这种选择只有在多线程并发的场景下才会出现,而多线程并发的目的一般是为了提高吞吐量和减少延迟响应,所以还是先看段测试代码和运行结果吧!
- import java.util.concurrent.CountDownLatch;
- import java.util.concurrent.atomic.AtomicInteger;
- public class TestVolatile {
- private static int CALC_TIME = 1000;
- private static final int THREAD_NUM = 100;
- private AtomicInteger ai;
- private int i;
- private volatile int vi;
-
- public TestVolatile(){
- ai = new AtomicInteger(0);
- i = 0;
- vi = 0;
- }
- public static void main(String[] args) throws InterruptedException {
- System.out.println("Calculation Times:" + CALC_TIME + " ----------------------");
- test();
-
- CALC_TIME = 10000;
- System.out.println("Calculation Times:" + CALC_TIME + " ----------------------");
- test();
-
- CALC_TIME = 100000;
- System.out.println("Calculation Times:" + CALC_TIME + " ----------------------");
- test();
-
- CALC_TIME = 1000000;
- System.out.println("Calculation Times:" + CALC_TIME + " ----------------------");
- test();
- }
- private static void test() throws InterruptedException {
- testAi();
-
- testI();
-
- testVi();
- }
- private static void testAi() throws InterruptedException {
- TestVolatile testVolatile = new TestVolatile();
- CountDownLatch begSignal = new CountDownLatch(1);
- CountDownLatch endSignal = new CountDownLatch(THREAD_NUM);
- for (int i = 0; i < THREAD_NUM; i++) {
- new Thread( testVolatile.new WorkerAI(begSignal, endSignal) ).start();
- }
- long startTime = System.currentTimeMillis();
-
- begSignal.countDown();
- endSignal.await();
-
- long endTime = System.currentTimeMillis();
-
- System.out.println("Total time consumed by atomic increment : " + (endTime-startTime));
- }
- private static void testI()
- throws InterruptedException {
- TestVolatile testVolatile = new TestVolatile();
- CountDownLatch begSignal = new CountDownLatch(1);
- CountDownLatch endSignal = new CountDownLatch(THREAD_NUM);
-
- for (int i = 0; i < THREAD_NUM; i++) {
- new Thread( testVolatile.new WorkerI(begSignal, endSignal) ).start();
- }
- long startTime = System.currentTimeMillis();
-
- begSignal.countDown();
- endSignal.await();
-
- long endTime = System.currentTimeMillis();
-
- System.out.println("Total time consumed by synchronized increment : " + (endTime-startTime));
- }
-
- private static void testVi()
- throws InterruptedException {
- TestVolatile testVolatile = new TestVolatile();
- CountDownLatch begSignal = new CountDownLatch(1);
- CountDownLatch endSignal = new CountDownLatch(THREAD_NUM);
-
- for (int i = 0; i < THREAD_NUM; i++) {
- new Thread( testVolatile.new WorkerVI(begSignal, endSignal) ).start();
- }
- long startTime = System.currentTimeMillis();
-
- begSignal.countDown();
- endSignal.await();
-
- long endTime = System.currentTimeMillis();
-
- System.out.println("Total time consumed by volatile increment : " + (endTime-startTime));
- }
- public void incrAi() {
- ai.getAndIncrement();
- }
- public synchronized void incrI() {
- i++;
- }
- /**
- * 这个函数不是线程安全,很可能得到错误的结果,这里只是为了测试读取volatile变量的效率
- */
- public void incrVi() {
- vi++;
- }
- class WorkerAI implements Runnable {
- private CountDownLatch beginSignal;
- private CountDownLatch endSignal;
- public WorkerAI(CountDownLatch begin, CountDownLatch end) {
- this.beginSignal = begin;
- this.endSignal = end;
- }
- @Override
- public void run() {
- try {
- beginSignal.await();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- for(int j=0; j<CALC_TIME; j++){
- incrAi();
- }
-
- endSignal.countDown();
- }
- }
- class WorkerI implements Runnable {
- private CountDownLatch beginSignal;
- private CountDownLatch endSignal;
- public WorkerI(CountDownLatch begin, CountDownLatch end) {
- this.beginSignal = begin;
- this.endSignal = end;
- }
- @Override
- public void run() {
- try {
- beginSignal.await();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- for(int j=0; j<CALC_TIME; j++){
- incrAi();
- }
- endSignal.countDown();
- }
- }
- class WorkerVI implements Runnable {
- private CountDownLatch beginSignal;
- private CountDownLatch endSignal;
- public WorkerVI(CountDownLatch begin, CountDownLatch end) {
- this.beginSignal = begin;
- this.endSignal = end;
- }
- @Override
- public void run() {
- try {
- beginSignal.await();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- for(int j=0; j<CALC_TIME; j++){
- incrVi();
- }
- endSignal.countDown();
- }
- }
- }
程序运行结果:
Calculation Times:1000 ---------------------- Total time consumed by atomic increment : 8 Total time consumed by synchronized increment : 6 Total time consumed by volatile increment : 5 Calculation Times:10000 ---------------------- Total time consumed by atomic increment : 23 Total time consumed by synchronized increment : 24 Total time consumed by volatile increment : 15 Calculation Times:100000 ---------------------- Total time consumed by atomic increment : 354 Total time consumed by synchronized increment : 360 Total time consumed by volatile increment : 148 Calculation Times:1000000 ---------------------- Total time consumed by atomic increment : 3579 Total time consumed by synchronized increment : 3608 Total time consumed by volatile increment : 1519
(怀疑自己的程序写得有问题,但暂时找不到问题,请大家帮忙拍砖!)
从测试结果看,原子变量的效率与synchronized同步操作效率差不多,感觉不到优势,volatile变量提升一倍的性能(当然++操作是有同步问题),所以如果volatile变量能满足需求优先使用volatile变量,原子变量次之。那什么时候适合使用volatile变量?专家推荐最佳实践是同时满足以下三个条件:
对变量的写入操作不依赖变量的当前值,或者能确保只有单个线程更新变量的值
改变量不会与其他状态变量一起组成不变性的条件
在访问变量时不需要加锁
个人实践总结:
满足条件的情况下使用volatile布尔变量,其他数据类型使用原子变量。
转载于:https://blog.51cto.com/stevex/1285964