Java中对AtomicInteger和int值在多线程下递增操作的测试
Java针对多线程下的数值安全计数器设计了一些类,这些类叫做原子类,其中一部分如下:
java.util.concurrent.atomic.AtomicBoolean; java.util.concurrent.atomic.AtomicInteger; java.util.concurrent.atomic.AtomicLong; java.util.concurrent.atomic.AtomicReference;
下面是一个对比 AtomicInteger与普通int值在多线程下的递增测试,使用的是junit4;
完整代码:
packagetest.java;
importjava.util.concurrent.CountDownLatch;
importjava.util.concurrent.atomic.AtomicInteger;
importorg.junit.Assert;
importorg.junit.Before;
importorg.junit.Test;
/**
*测试AtomicInteger与普通int值在多线程下的递增操作
*/
publicclassTestAtomic{
//原子Integer递增对象
publicstaticAtomicIntegercounter_integer;//=newAtomicInteger(0);
//一个int类型的变量
publicstaticintcount_int=0;
@Before
publicvoidsetUp(){
//所有测试开始之前执行初始设置工作
counter_integer=newAtomicInteger(0);
}
@Test
publicvoidtestAtomic()throwsInterruptedException{
//创建的线程数量
intthreadCount=100;
//其他附属线程内部循环多少次
intloopCount=10000600;
//控制附属线程的辅助对象;(其他await的线程先等着主线程喊开始)
CountDownLatchlatch_1=newCountDownLatch(1);
//控制主线程的辅助对象;(主线程等着所有附属线程都运行完毕再继续)
CountDownLatchlatch_n=newCountDownLatch(threadCount);
//创建并启动其他附属线程
for(inti=0;i<threadCount;i++){
Threadthread=newAtomicIntegerThread(latch_1,latch_n,loopCount);
thread.start();
}
longstartNano=System.nanoTime();
//让其他等待的线程统一开始
latch_1.countDown();
//等待其他线程执行完
latch_n.await();
//
longendNano=System.nanoTime();
intsum=counter_integer.get();
//
Assert.assertEquals("sum不等于threadCount*loopCount,测试失败",
sum,threadCount*loopCount);
System.out.println("--------testAtomic();预期两者相等------------");
System.out.println("耗时:"+((endNano-startNano)/(1000*1000))+"ms");
System.out.println("threadCount="+(threadCount)+";");
System.out.println("loopCount="+(loopCount)+";");
System.out.println("sum="+(sum)+";");
}
@Test
publicvoidtestIntAdd()throwsInterruptedException{
//创建的线程数量
intthreadCount=100;
//其他附属线程内部循环多少次
intloopCount=10000600;
//控制附属线程的辅助对象;(其他await的线程先等着主线程喊开始)
CountDownLatchlatch_1=newCountDownLatch(1);
//控制主线程的辅助对象;(主线程等着所有附属线程都运行完毕再继续)
CountDownLatchlatch_n=newCountDownLatch(threadCount);
//创建并启动其他附属线程
for(inti=0;i<threadCount;i++){
Threadthread=newIntegerThread(latch_1,latch_n,loopCount);
thread.start();
}
longstartNano=System.nanoTime();
//让其他等待的线程统一开始
latch_1.countDown();
//等待其他线程执行完
latch_n.await();
//
longendNano=System.nanoTime();
intsum=count_int;
//
Assert.assertNotEquals(
"sum等于threadCount*loopCount,testIntAdd()测试失败",
sum,threadCount*loopCount);
System.out.println("-------testIntAdd();预期两者不相等---------");
System.out.println("耗时:"+((endNano-startNano)/(1000*1000))+"ms");
System.out.println("threadCount="+(threadCount)+";");
System.out.println("loopCount="+(loopCount)+";");
System.out.println("sum="+(sum)+";");
}
//线程
classAtomicIntegerThreadextendsThread{
privateCountDownLatchlatch=null;
privateCountDownLatchlatchdown=null;
privateintloopCount;
publicAtomicIntegerThread(CountDownLatchlatch,
CountDownLatchlatchdown,intloopCount){
this.latch=latch;
this.latchdown=latchdown;
this.loopCount=loopCount;
}
@Override
publicvoidrun(){
//等待信号同步
try{
this.latch.await();
}catch(InterruptedExceptione){
e.printStackTrace();
}
//
for(inti=0;i<loopCount;i++){
counter_integer.getAndIncrement();
}
//通知递减1次
latchdown.countDown();
}
}
//线程
classIntegerThreadextendsThread{
privateCountDownLatchlatch=null;
privateCountDownLatchlatchdown=null;
privateintloopCount;
publicIntegerThread(CountDownLatchlatch,
CountDownLatchlatchdown,intloopCount){
this.latch=latch;
this.latchdown=latchdown;
this.loopCount=loopCount;
}
@Override
publicvoidrun(){
//等待信号同步
try{
this.latch.await();
}catch(InterruptedExceptione){
e.printStackTrace();
}
//
for(inti=0;i<loopCount;i++){
count_int++;
}
//通知递减1次
latchdown.countDown();
}
}
}
普通PC机上的执行结果类似如下:
--------------testAtomic();预期两者相等------------------- 耗时:85366ms threadCount=100; loopCount=10000600; sum=1000060000; --------------testIntAdd();预期两者不相等------------------- 耗时:1406ms threadCount=100; loopCount=10000600; sum=119428988;
从中可以看出,AtomicInteger操作与int操作的效率大致相差在50-80倍上下,当然,int很不消耗时间,这个对比只是提供一个参照。
如果确定是单线程执行,那应该使用int;而int在多线程下的操作执行的效率还是蛮高的,10亿次只花了1.5秒钟;
(假设CPU是2GHZ,双核4线程,理论最大8GHZ,则每秒理论上有80亿个时钟周期,
10亿次Java的int增加消耗了1.5秒,即120亿次运算,算下来每次循环消耗CPU周期12个;
个人觉得效率不错,C语言也应该需要4个以上的时钟周期(判断,执行内部代码,自增判断,跳转)
前提是:JVM和CPU没有进行激进优化.
)
而AtomicInteger效率其实也不低,10亿次消耗了80秒,那100万次大约也就是千分之一,80毫秒的样子.