目錄
前文
多線程之間常見同步方式 :Synchronized / Lock / volitial + CAS 等,在不同的併發量和不同併發場景下性能不盡相同,尤其是Synchronized在不斷經過優化之後性能得到了提升,而且無鎖化CAS編程也成爲了一些框架提升性能的手段,在JDK 8中,處理已有的原子類之外,添加了LongAdder和LongAccumulator原子類,除去應用場景不說,性能要比原始的原子類性能要好。
LongAdder的實現原理是將原本的value,切分成了多個數組,每個線程單獨操作數組中自己對應的值,在獲取LongAdder的結果的時候,需要對數組中所有的值進行求和sum操作,LongAccumulator是LongAdder的功能增強版本,原理類似。
正文
以下分別對Synchronized、AtomicLong和LongAdder在多線程下的性能進行簡單測試。
0. 測試環境
測試環境就不詳細給出了,個人mac pro電腦,編譯器:idea ,jdk 8……
1. Synchronized 測試
所需代碼:
private static final int MAX_THREADS = 3; //線程數
private static final int TARGET_COUNT = 10000000; //目標總數
//用於統計完成子線程數目
static CountDownLatch cdlsync = new CountDownLatch(MAX_THREADS);
//所需要同步的變量
private long count = 0;
//同步的方法
protected synchronized long getCount(){
return count;
}
protected synchronized long inc(){
//如果不在這裏進行越界判斷,會出現超出TARGET_COUNT的情況
if(count<TARGET_COUNT){
return ++count;
}
return count;
}
//多線程類
public class SynThread implements Runnable{
private CountDownLatch cdlsync = null;
private long start_time;
private MuilThreadCompare compare=null;
public SynThread(MuilThreadCompare o,CountDownLatch cdlsync,long start_time){
this.compare = o;
this.cdlsync = cdlsync;
this.start_time = start_time;
}
@Override
public void run() {
long re = compare.getCount();
while(re<TARGET_COUNT){
re = compare.inc();
}
long endtime=System.currentTimeMillis();
System.out.println("SyncThread spend:"+(endtime-start_time)+"ms"+" v="+re);
cdlsync.countDown();
}
}
//測試方法
public void testSyn() throws InterruptedException{
ExecutorService pool = Executors.newFixedThreadPool(MAX_THREADS);
long starttime=System.currentTimeMillis();
for (int i = 0; i < MAX_THREADS; i++) {
SynThread thread = new SynThread(this,cdlsync,starttime);
pool.submit(thread);
}
cdlsync.await();
pool.shutdown();
}
結果輸入如下所示,使用3個線程,時間穩定在320ms左右,未出現結果越界情況:
2. AtomicLong測試
所需代碼如下:
static CountDownLatch cdlatomic=new CountDownLatch(MAX_THREADS);
//原子類
private AtomicLong acount =new AtomicLong(0L);
//線程類
public class AtomicLongThread implements Runnable{
private long start_time;
private AtomicLong atomic = null;
public AtomicLongThread(AtomicLong atomic,long start_time){
this.atomic = atomic;
this.start_time = start_time;
}
@Override
public void run() {
long temp_count = atomic.get();
while(temp_count<TARGET_COUNT){
// 不要用incrementAndGet 方法,否則會出現結果越界情況
//temp_count = atomic.incrementAndGet();
atomic.compareAndSet(temp_count,temp_count+1);
temp_count++;
}
long end_time=System.currentTimeMillis();
System.out.println("AtomicThread spend:"+(end_time-start_time)+"ms"+" v="+temp_count);
cdlatomic.countDown();
}
}
//測試方法:
public void testAtomicLong() throws InterruptedException{
ExecutorService pool = Executors.newFixedThreadPool(MAX_THREADS);
long starttime=System.currentTimeMillis();
for (int i = 0; i < MAX_THREADS; i++) {
AtomicLongThread thread = new AtomicLongThread(acount,starttime);
pool.submit(thread);
}
cdlatomic.await();
pool.shutdown();
}
結果如下圖所示,穩定在370ms左右,未出現結果越界情況,一開始的時候使用incrementAndGet API更新值會出現結果越界的情況:
3. LongAdder測試
所需代碼:
static CountDownLatch cdladdr=new CountDownLatch(MAX_THREADS);
//新原子類
private LongAdder lacount=new LongAdder();
//線程類
public class LongAdderThread implements Runnable{
private long start_time;
private LongAdder longAdder = null;
public LongAdderThread(long start_time,LongAdder longAdder){
this.start_time = start_time;
this.longAdder = longAdder;
}
@Override
public void run() {
long v = longAdder.sum();
while(v<TARGET_COUNT){
longAdder.increment();
v=longAdder.sum();
}
long end_time=System.currentTimeMillis();
System.out.println("LongAdder spend:"+(end_time-start_time)+"ms"+" v="+v);
cdladdr.countDown();
}
}
//測試方法
public void testLongAdder() throws InterruptedException{
ExecutorService pool = Executors.newFixedThreadPool(MAX_THREADS);
long starttime=System.currentTimeMillis();
for (int i = 0; i < MAX_THREADS; i++) {
LongAdderThread thread = new LongAdderThread(starttime,lacount);
pool.submit(thread);
}
cdlatomic.await();
pool.shutdown();
}
結果如下圖所示,時間穩定在180ms左右,但是存在結果值越界的情況,應爲:
在更新值的時候,先獲取的value的值,然後原子增加,期間可能插入其他線程的操作,導致結果越界。
總結
測試的線程數目不多,3個線程,在低線程數目的情況下,新的原子類表現要比普通原子類和Synchronized性能優良,類推多線程數更多的情況下,LongAdder表現會更好,增加到10線程數的時候,LongAdder:200ms AtomicLong:1330ms Synchronized:384ms ,繼續增加線程數差距會越大。
證明LongAdder性能優良的同時,比較意外的是Synchronized性能同樣很好,甚至超過了普通的原子類,限制於機器性能,沒有測試測試線程數更多的情況,看來低線程的時候,Synchronized要比自旋CAS要好。