原子类
在多线程环境下,常用累加操作方式是使用原子类进行累加,例如AtomicInteger、AtomicLong。但是使用原子类在多线程高竞争的情况下,CAS会经常失败,并发效率会大大降低。
因为CAS操作失败后要自旋再次进行替换,这样失败的线程就会大量消耗CPU资源。所以在高并发的场景下使用原子类累加器并不是很好的选择。
Striped64
Striped64是一种高并发累加器,有效解决了原子类累加的弊端。Striped64将线程竞争的操作分散开来,每个线程操作一个cell,而sum则等于base和所有cell值的和。
性能比较
开启10个线程,并发执行累加操作,每个线程加10000000。
- 使用AtomicLong
public static void main(String[] args) {
AtomicLong atomicLong = new AtomicLong(0);
CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
CountDownLatch countDownLatch = new CountDownLatch(10);
long l = System.currentTimeMillis();
for (int i=0;i<10;i++){
new Thread(new Runnable() {
@Override
public void run() {
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
int i=10000000;
while(i>0){
atomicLong.incrementAndGet();
i--;
}
countDownLatch.countDown();
}
}).start();
}
try {
countDownLatch.await();
long l2 = System.currentTimeMillis();
System.out.println(l2-l);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicLong.get());
}
计算结果:
1964ms
100000000
- 使用LongAdder
public static void main(String[] args) {
LongAdder longAdder = new LongAdder();
CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
CountDownLatch countDownLatch = new CountDownLatch(10);
long l = System.currentTimeMillis();
for (int i=0;i<10;i++){
new Thread(new Runnable() {
@Override
public void run() {
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
int i=10000000;
while(i>0){
longAdder.add(1);
i--;
}
countDownLatch.countDown();
}
}).start();
}
try {
countDownLatch.await();
long l2 = System.currentTimeMillis();
System.out.println((l2-l)+"ms");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(longAdder.sum());
}
执行结果:
266ms
100000000
比较两者的性能,Striped64比原子类要快约10倍。
LongAdder
当线程来进行添加操作的,会根据线程ID定位到具体的cell,线程再对cell进行CAS操作,进行累加。这样各个线程就不用产生激烈竞争导致频繁CAS失败。对于JVM中最高并发线程数等与机器可用CPU核数,所以cells数组的长度也不会很长,进行数组求和也很快。
Striped64的继承类有LongAdder,以LongAdder累加流程为例: