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

付威     2019-07-22   5122   14min  

今天在学习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. 使用反射窃取字段的值,也是这篇博客使用的方式,代码如下:

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

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

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

对应的测试类:

     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. 线程不安全的计数器

     private long count=0;
     @Override
     public void increase() {
         count++;
     }
    
     @Override
     public long getCounter() {
         return count;
     }
    
    

    运行结果:

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

  2. synchronized关键字计数器:

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

    运行结果:

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

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

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

    运行结果:

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

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

     public class AutoCounter implements ICounter {
           private AtomicLong count=new AtomicLong(0);
           @Override
           public    void increase() {
                count.incrementAndGet();
           }
    
           @Override
           public long getCounter() {
                return count.get();
           }
      }
    

    运行结果:

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

  5. 手写CAS 方式计数器

     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
    

  6. 手写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

(本文完)

作者:付威

博客地址:http://blog.laofu.online

如果觉得对您有帮助,可以下方的RSS订阅,谢谢合作

如有任何知识产权、版权问题或理论错误,还请指正。

本文是付威的网络博客原创,自由转载-非商用-非衍生-保持署名,请遵循:创意共享3.0许可证

交流请加群113249828: 点击加群   或发我邮件 laofu_online@163.com

付威

获得最新的博主文章,请关注上方公众号