写了一个性能比AtomicLong性能还高的计数器

今天在学习CAS的时候,想手写一个CAS的计数器,与JDK中的Atomic(AtomicLong,AtomicInteger等)系列的做个比较,本想性能应该能比JDK要差一丢丢,但却加了一个让线程让出时间片的代码,性能反而更高。

由于使用java中的Unsafe类,存在安全问题,直接使用会抛出SecurityException异常,所以Unsage无法直接在代码中调用,有两种方法可以解决这个问题:

  1. 增加调用的参数,让JVM信任,运行程序时候,增加java -Xbootclasspath:/usr/jdkxxxx/jre/lib/rt.jar:. com.mishadoff.magic.UnsafeClient

  2. 使用反射窃取字段的值,也是这篇博客使用的方式,代码如下:

    1
    2
    3
    Field f = Unsafe.class.getDeclaredField("theUnsafe");
    f.setAccessible(true);
    unsafe = (Unsafe) f.get(null);

为了更好的对比计数器的性能,首先定义了一个接口:

1
2
3
4
public interface ICounter {
public void increase();
public long getCounter();
}

对应的测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private static void testCounter(ICounter counter, String counterName) throws InterruptedException {
int nThreads = 10;
int maxValue = 1000000;
ExecutorService service = Executors.newFixedThreadPool(nThreads);
// creating instance of specific counter
long before = System.currentTimeMillis();
for (int i = 0; i < nThreads; i++) {
service.submit(() -> {
for (int j = 0; j < maxValue; j++) {
counter.increase();
}
});
}

service.shutdown();
service.awaitTermination(1, TimeUnit.MINUTES);
long after = System.currentTimeMillis();
System.out.println(counterName + " Counter计算结果: " + counter.getCounter());
System.out.println(counterName + " Counter计算耗时:" + (after - before));
System.out.println("================================================");
}

分别写了各种不同的锁的版本的计数器:

  1. 线程不安全的计数器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    private long count=0;
    @Override
    public void increase() {
    count++;
    }

    @Override
    public long getCounter() {
    return count;
    }

    运行结果:

    1
    2
    Counter Counter计算结果: 9824109
    Counter Counter计算耗时:120

  2. synchronized关键字计数器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class SyncCounter implements ICounter {
    private long count=0;
    @Override
    public synchronized void increase() {
    count++;
    }

    @Override
    public long getCounter() {
    return count;
    }
    }

    运行结果:

    1
    2
    SyncCounter Counter计算结果: 100000000
    SyncCounter Counter计算耗时:4570

  3. 使用读写锁(ReentrantLockCounter)计数器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class ReentrantLockCounter  implements ICounter{
    private long counter = 0;
    private ReentrantReadWriteLock.WriteLock lock = new ReentrantReadWriteLock().writeLock();
    @Override
    public void increase() {
    lock.lock();
    counter++;
    lock.unlock();
    }

    @Override
    public long getCounter() {
    return counter;
    }
    }

    运行结果:

    1
    2
    ReentrantLockCounter Counter计算结果: 100000000
    ReentrantLockCounter Counter计算耗时:3734

  4. 使用JDK中的CAS方式的计数器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class AutoCounter implements ICounter {
    private AtomicLong count=new AtomicLong(0);
    @Override
    public void increase() {
    count.incrementAndGet();
    }

    @Override
    public long getCounter() {
    return count.get();
    }
    }

    运行结果:

    1
    2
    AutoCounter Counter计算结果: 100000000
    AutoCounter Counter计算耗时:2930

  5. 手写CAS 方式计数器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class RzCounter implements ICounter {
    private volatile long counter = 0;
    private Unsafe unsafe;
    private long offset;

    public RzCounter() {
    try {
    Field f = Unsafe.class.getDeclaredField("theUnsafe");
    f.setAccessible(true);
    unsafe = (Unsafe) f.get(null);
    offset = unsafe.objectFieldOffset(RzCounter.class.getDeclaredField("counter"));
    } catch (Exception e) {
    e.printStackTrace();
    }

    }

    public void increase() {
    unsafe.getAndAddLong(this, offset, 1);
    }

    public long getCounter() {
    return counter;
    }
    }

    运行结果:

    1
    2
    RzCounter Counter计算结果: 100000000
    RzCounter Counter计算耗时:3139

  6. 手写CAS方式计数器2:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class RzCounter1 implements ICounter {
    private volatile long counter = 0;
    private Unsafe unsafe;
    private long offset;

    public RzCounter1() {
    try {
    Field f = Unsafe.class.getDeclaredField("theUnsafe");//
    f.setAccessible(true);
    unsafe = (Unsafe) f.get(null);
    offset = unsafe.objectFieldOffset(RzCounter1.class.getDeclaredField("counter"));
    } catch (Exception e) {
    e.printStackTrace();
    }

    }

    public void increase() {
    long before = unsafe.getLongVolatile(this, offset);
    while (!unsafe.compareAndSwapLong(this, offset, before, before + 1))
    {
    before = unsafe.getLongVolatile(this, offset);
    Thread.yield();
    }
    }

    public long getCounter() {
    return counter;
    }
    }

    运行结果:

    1
    2
    RzCounter1 Counter计算结果: 100000000
    RzCounter1 Counter计算耗时:1565

经过多次测试,性能的排序始终是经过多次测试,性能的排序始终是 CAS(RzCounter)<原生CAS<第二种CAS(RzCounter1) ,如果真是这样的话,jdk为什么不在incrementAndGet增加一个Thread.yield()

源码下载地址:https://u7704756.pipipan.com/fs/7704756-389104478

写了一个性能比AtomicLong性能还高的计数器

http://blog.laofu.online/2019-07-22-cas-rzCounter/

作者

付威

发布于

2019-07-22

更新于

2019-07-22

许可协议

评论