应用场景
我们知道在实际的应用场景中,可能会对某个商品进行浏览次数进行迭代,或者抖音视频的点击,那么如何高效记录呢,首先如果是使用普通的num 进行多线程操作的话,那么一定会带来数据一致性问题,所以一般通过syn\lock,但是因为有加锁和解锁的操作,以及线程竞争过多的时候,导致线程上下切换。那么又没有一种高效的方式呢,就是使用无锁编程原子类,atomicInteger但是因为是使用CAS并且针对的是同一个数据进行cas操作,操作数据的粒度是一个,所以进一步的方式是使用longAdder,主要原理就是将数据的操作粒度分散,类似于hashmap的散列表方式。
code
/**
* @author qxlx
* @date 2023/10/15 9:52 AM
*/
class MyNumber {
public Long num = new Long(0);
public synchronized long synAddNumer() {
return num++;
}
public AtomicInteger atomicInteger = new AtomicInteger(0);
public int atomicIntegerAddNumer() {
return atomicInteger.getAndIncrement();
}
public LongAdder longAdder = new LongAdder();
public int longAdderAddNumber(){
longAdder.increment();
return longAdder.intValue();
}
}
public class LongAddrCalcDemo {
public static final int THREAD_NUM = 50;
public static final int ADD_NUM = 1000000;
public static void main(String[] args) throws InterruptedException {
MyNumber myNumber = new MyNumber();
StopWatch stopWatch = new StopWatch();
CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);
CountDownLatch countDownLatch2 = new CountDownLatch(THREAD_NUM);
CountDownLatch countDownLatch3 = new CountDownLatch(THREAD_NUM);
stopWatch.start("1.加锁方式耗时");
for (int i = 0; i < THREAD_NUM; i++) {
new Thread(()-> {
for (int j = 0; j < ADD_NUM ; j++) {
long numer = myNumber.synAddNumer();
}
countDownLatch.countDown();
}).start();
}
countDownLatch.await();
System.out.println("1.加锁方式,num:"+myNumber.num);
stopWatch.stop();
stopWatch.start("2.atomic");
for (int i = 0; i < THREAD_NUM; i++) {
new Thread(()-> {
for (int j = 0; j < ADD_NUM ; j++) {
long numer = myNumber.atomicIntegerAddNumer();
}
countDownLatch2.countDown();
}).start();
}
countDownLatch2.await();
System.out.println("2.atomic,num:"+myNumber.atomicInteger);
stopWatch.stop();
stopWatch.start("3.longadder");
for (int i = 0; i < THREAD_NUM; i++) {
new Thread(()-> {
for (int j = 0; j < ADD_NUM ; j++) {
long numer = myNumber.longAdderAddNumber();
}
countDownLatch3.countDown();
}).start();
}
countDownLatch3.await();
System.out.println("3.longadder,num:"+myNumber.longAdder);
stopWatch.stop();
System.out.println("date:"+stopWatch.prettyPrint());
}
}
原理
源码解读
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
if ((as = cells) != null || !casBase(b = base, b + x)) {
boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x)))
longAccumulate(x, null, uncontended);
}
}