CAS 底层实现是在一个死循环中不断地尝试修改目标值,直到修改成功。如果竞争不激烈的时候,修改成功率很高,否则失败率很高。在失败的时候,这些重复的原子性操作会耗费性能。(不停的自旋,进入一个无限重复的循环中)
LongAdder
LongAdder,尝试使用分段CAS以及自动分段迁移的方式来大幅度提升多线程高并发执行CAS操作的性能!(JDK1.8新特性)
- 在LongAdder的底层实现中,首先有一个base值,刚开始多线程来不停的累加数值,都是对base进行累加的。
- 接着如果发现并发更新的线程数量过多,在发生竞争的情况下,会有一个Cell数组用于将不同线程的操作离散到不同的节点上去 ==(会根据需要扩容,最大为CPU核)==就会开始施行分段CAS的机制,也就是内部会搞一个Cell数组,每个数组是一个数值分段。
- 这时,让大量的线程分别去对不同Cell内部的value值进行CAS累加操作,这样就把CAS计算压力分散到了不同的Cell分段数值中了!
- 这样就可以大幅度的降低多线程并发更新同一个数值时出现的无限循环的问题,大幅度提升了多线程并发更新数值的性能和效率!
- 而且他内部实现了自动分段迁移的机制,也就是如果某个Cell的value执行CAS失败了,那么就会自动去找另外一个Cell分段内的value值进行CAS操作。
- 这样也解决了线程空旋转、自旋不停等待执行CAS操作的问题,让一个线程过来执行CAS时可以尽快的完成这个操作。
- 最后,如果你要从LongAdder中获取当前累加的总值,就会把base值和所有Cell分段数值加起来返回给你。
AtomicLong VS LongAdder
public class LongAdderVSAtomicLongTest {
public static void main(String[] args) {
testAtomicLongVSLongAdder(1,10000000);
testAtomicLongVSLongAdder(10,10000000);
testAtomicLongVSLongAdder(20,10000000);
testAtomicLongVSLongAdder(30,10000000);
testAtomicLongVSLongAdder(40,10000000);
}
static void testAtomicLongVSLongAdder(final int threadCount,final int times){
try{
System.out.println("threadCount ="+threadCount+" , times ="+times);
long start = System.currentTimeMillis();
testLongAdder(threadCount,times);
System.out.println("LongAdder 耗时 "+(System.currentTimeMillis()-start)+"ms");
long start2=System.currentTimeMillis();
testAtomicLong(threadCount,times);
System.out.println("AtomicLong 耗时 "+(System.currentTimeMillis()-start2)+"ms");
}catch (InterruptedException e){
e.printStackTrace();
}
}
static void testAtomicLong(final int threadCount,final int times)throws InterruptedException{
AtomicLong atomicLong = new AtomicLong();
List<Thread> list = new ArrayList<>();