写了一个性能比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
    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
    31
    32
    33
    34
    35
    36
    37
    38
    39
              Field f = Unsafe.class.getDeclaredField("theUnsafe");
    f.setAccessible(true);
    unsafe = (Unsafe) f.get(null);
    ```

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

    ``` java
    public interface ICounter {
    public void increase();
    public long getCounter();
    }
    ```

    对应的测试类:

    ``` java
    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
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
        private long count=0;
    @Override
    public void increase() {
    count++;
    }

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

    ```
    运行结果:

    ``` cte
    Counter Counter计算结果: 9824109
    Counter Counter计算耗时:120
    ```

    -----------------------------------

    2. `synchronized`关键字计数器:

    ``` java
    public class SyncCounter implements ICounter {
    private long count=0;
    @Override
    public synchronized void increase() {
    count++;
    }

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

    ```
    运行结果:

    ``` cte
    SyncCounter Counter计算结果: 100000000
    SyncCounter Counter计算耗时:4570
    ```

    -----------------------------------

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

    ``` java
    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;
    }
    }
    ```
    运行结果:

    ``` cte
    ReentrantLockCounter Counter计算结果: 100000000
    ReentrantLockCounter Counter计算耗时:3734
    ```
    -----------------------------------------------

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

    ``` java
    public class AutoCounter implements ICounter {
    private AtomicLong count=new AtomicLong(0);
    @Override
    public void increase() {
    count.incrementAndGet();
    }

    @Override
    public long getCounter() {
    return count.get();
    }
    }
    ```
    运行结果:

    ``` cte
    AutoCounter Counter计算结果: 100000000
    AutoCounter Counter计算耗时:2930
    ```
    --------------------------------------------------

    5. 手写`CAS` 方式计数器

    ``` java
    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;
    }
    }

    运行结果:

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

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

      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;
           }
      }
    

    运行结果:

      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

许可协议

评论