Welcome 微信登录

首页 / 软件开发 / JAVA / 使用Volatile变量还是原子变量

使用Volatile变量还是原子变量2013-12-19volatile变量

在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;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic void run() {try {beginSignal.await();} catch (InterruptedException e) {e.printStackTrace();}for(int j=0; j<CALC_TIME; j++){incrVi();}endSignal.countDown();}}}