参考文档:https://baijiahao.baidu.com/s?id=1742540809477784106&wfr=spider&for=pc
一、常用的并发计数方法
1、synchronized
synchronized早期是一个重量级锁,因为线程竞争锁会引起操作系统用户态和内核态切换,浪费资源,效率不高。jdk1.6对 synchronized 做了性能优化,它会经历偏向锁,轻量级锁,最后才到重量级锁,性能提升显著。jdk1.7的 ConcurrentHashMap 是基于ReentrantLock的实现了锁,但在jdk1.8之后又替换成了 synchronized,籍此可见JVM团队对synchronized的信心;
2、原子更新整型 AtomicInteger、AtomicLong
JDK1.5提供了并发的Integer/Long的操作工具类 AtomicInteger、AtomicLong。AtomicLong底层是一个乐观锁,不用阻塞线程,其利用底层操作系统的CAS来保证原子性,即在一个死循环内不断执行CAS操作,直到成功。其缺陷是当并发较大时,大量线程不断CAS,存在多次的CAS操作失败,使CPU升高,效率降低。
AtomicLong value = new AtomicLong(0);
value.incrementAndGet();
3、LongAdder [单机]
在JDK8中新增了LongAdder,这是针对Long类型的数据的操作工具类。在ConcurrentHashMap中,对Map分割成多个segment,这样多个Segment的操作就可以并行执行,从而提高性能。在JDK8中,LongAdder与ConcurrentHashMap类似,将内部操作数据value分离成一个Cell数组,每个线程访问时,通过Hash等算法映射到其中一个Cell上。 计算最终的数据结果,则是各个Cell数组的累计求和。
4、Redisson分布式累加器 [分布式]
基于Redis的Redisson分布式长整型累加器(LongAdder)采用了与java.util.concurrent.atomic.LongAdder 类似的接口。通过利用客户端内置的 LongAdder 对象,为分布式环境下递增和递减操作提供了很高的性能。其性能最高比分布式 AtomicLong 对象快 10^4 倍。
RLongAddr itheimaLongAddr = redission.getLongAddr(“itheimaLongAddr”);
itheimaLongAddr.add(100);
itheimaLongAddr.increment();
itheimaLongAddr.increment();
itheimaLongAddr.sum();
基于Redis的Redisson分布式双精度浮点累加器(DoubleAdder)采用了与 java.util.concurrent.atomic.DoubleAdder 类似的接口。通过利用客户端内置的 DoubleAdder 对象,为分布式环境下递增和递减操作提供了很高的性能。其性能最高比分布式 AtomicDouble 对象快 10^4 倍。
RLongDouble itheimaDouble = redission.getLongDouble(“itheimaLongDouble”);
itheimaDouble.add(100);
itheimaDouble.increment();
itheimaDouble.increment();
itheimaDouble.sum();
二、并发计数方法性能测试
1、测试程序
private long count = 0;@Testpublic void counterCompare() {this.counterCousum(1, 1000 * 1000);this.counterCousum(20, 1000 * 1000);this.counterCousum(40, 1000 * 1000);this.counterCousum(60, 1000 * 1000);this.counterCousum(100, 1000 * 1000);}/*** @param threadCount 线程数量* @param increTimes 每个线程自增次数*/private void counterCousum(int threadCount, int increTimes) {long start;try {System.out.println(String.format("线程数量: %s, 自增次数: %s", threadCount, increTimes));start = System.currentTimeMillis();this.synchronizedTest(threadCount, increTimes);System.out.println("Synchronized 耗时: " + (System.currentTimeMillis() - start));start = System.currentTimeMillis();this.atomicLongTest(threadCount, increTimes);System.out.println("AtomicLong 耗时: " + (System.currentTimeMillis() - start));start = System.currentTimeMillis();this.longAdderTest(threadCount, increTimes);System.out.println("LongAdder 耗时: " + (System.currentTimeMillis() - start) + "\n");} catch (Exception e) {e.printStackTrace();}}private void atomicLongTest(int threadCount, int times) throws InterruptedException {AtomicLong count = new AtomicLong();List<Thread> threadList = new ArrayList<>();for (int i = 0; i < threadCount; i++) {threadList.add(new Thread(()-> {for (int j = 0; j < times; j++) {count.incrementAndGet();}}));}for (Thread thread : threadList) {thread.start();}for (Thread thread : threadList) {thread.join();}}private void synchronizedTest(int threadCount, int times) throws InterruptedException {List<Thread> threadList = new ArrayList<>();for (int i = 0; i < threadCount; i++) {threadList.add(new Thread(()-> {for (int j = 0; j < times; j++) {add();}}));}for (Thread thread : threadList) {thread.start();}for (Thread thread : threadList) {thread.join();}}private synchronized void add() {count++;}private void longAdderTest(int threadCount, int times) throws InterruptedException {LongAdder count = new LongAdder();List<Thread> threadList = new ArrayList<>();for (int i = 0; i < threadCount; i++) {threadList.add(new Thread(()-> {for (int j = 0; j < times; j++) {count.increment();}}));}for (Thread thread : threadList) {thread.start();}for (Thread thread : threadList) {thread.join();}}
2、测试结果
synchronized、AtomicLong的耗时随着并发量的增大而增大,LongAdder的耗时保持相对稳定,性能优越 !!!