专栏首页用户7113604的专栏synchronized和ReentrantLock的性能比较
原创

synchronized和ReentrantLock的性能比较

重要说明:以下内容和观点基于本人PC的测试结果,数据量不大,并且分析也不够深入。仅供学习参考。

最近写了个例子,比较了一下synchronized和ReentrantLock的性能,分享一下数据和个人观点。

例子

public class SycLockTest {
  static String case1File = "c1.txt";
  static String case2File = "c2.txt";
  static volatile FileWriter caseWriter;
  static BlockingQueue<Long> dataQ;
  static String fileName="a.txt";
  static CountDownLatch countdown;
  static Object lock =new Object();
  static Lock reentrantLock = new ReentrantLock();
  static int x=10;
  volatile static List<Long> record;
  static int before = 500;
  static int redo = 50;
  static int vl=0;
  static BlockingQueue bq=new ArrayBlockingQueue(1000);
  static Object[] abc= new Object[100];
  static void fgh(){
    cal(before);
    long start = System.nanoTime();
    synchronized (lock){
      cal(redo);
      //writeFile();
      //add();
    }
    long cost =System.nanoTime()-start;
    addRecord(cost);
    dataQ.add(cost);
    //System.out.println(Thread.currentThread().getName());
    countdown.countDown();
  }

  static void efg(){
    cal(before);
    long start = System.nanoTime();
    reentrantLock.lock();
    cal(redo);
    //writeFile();
    //add();
    reentrantLock.unlock();
    long cost =System.nanoTime()-start;
    addRecord(cost);
    dataQ.add(cost);
    //System.out.println(Thread.currentThread().getName());
    countdown.countDown();
  }

  static void add(){
//    vl++;
//    vl--;
//    bq.add(lock);
//    bq.remove(lock);
//    bq.add(lock);
//    bq.remove(lock);
    for(int a=0;a<abc.length;a++){
      abc[a]=lock;
    }
  }
  static void cal(int repeat){
    while (repeat-->0){
      x= 1<<32;
    }
  }

  static void writeFile() {
    try(FileWriter fw = new FileWriter(fileName)){
      fw.write("xxxxxxxxxxxxxxxxxx");
      fw.write(System.lineSeparator());
      fw.write("yyyyyyyyyyyyyy");
    } catch (IOException e) {

    }
  }
  synchronized static void addRecord(long value){
    record.add(value);
  }
  static void startCase(int tCount,int reCall, Runnable runnable, List<Thread> threads,String aaa) throws InterruptedException {
    long sum=0;
    while(reCall-->0){
      threads.clear();
      record.clear();
      countdown = new CountDownLatch(tCount);
      for(int i=0;i<tCount;i++){
        Thread t = new Thread(runnable);
        t.setName(aaa+"-"+i);
        threads.add(t);
      }

      threads.parallelStream().forEach(t->t.start());
      countdown.await();
      long cost =  record.stream().reduce(0L,(x,y)->Long.sum(x,y));
      sum = Long.sum(sum,Math.round(cost));
    }
    System.out.println(sum);
  }

  static void writeResult()  {
    try {
      while (true) {
        long value = dataQ.take();
        caseWriter.write(value + System.lineSeparator());
      }
    }catch (Exception e){}
  }
  public static void main(String[] args) throws InterruptedException, IOException {
    int thCount = 20;//thread count
    int inputReCall = 500;//repeat count
    if(args.length>0){
      thCount = Integer.valueOf(Optional.ofNullable(args[0]).orElse("30"));
    }
    if(args.length>1){
      inputReCall = Integer.valueOf(Optional.ofNullable(args[1]).orElse("10"));
    }
    Files.deleteIfExists(Paths.get(fileName));
    Files.createFile(Paths.get(fileName));
    record = new CopyOnWriteArrayList();
    List<Thread> threads = new ArrayList<>(thCount);
    dataQ = new ArrayBlockingQueue<>(thCount*inputReCall);
    Thread dataWriter = new Thread(SycLockTest::writeResult);
    dataWriter.start();
    Files.deleteIfExists(Paths.get(case1File));
    Files.createFile(Paths.get(case1File));
    try{
      caseWriter = new FileWriter(case1File,true);
      startCase(thCount, inputReCall, SycLockTest::fgh, threads, "pppppppp");
    }catch(Exception e){ }finally {
      if(caseWriter!=null) caseWriter.close();
    }
    Files.deleteIfExists(Paths.get(case2File));
    Files.createFile(Paths.get(case2File));
    try {
      caseWriter = new FileWriter(case2File,true);
      startCase(thCount, inputReCall, SycLockTest::efg, threads, "qqqqqqqqqq");
    }catch(Exception e){ }finally {
        if(caseWriter!=null) caseWriter.close();
      }
  }

}

本例中有位移运算和写文件操作。本人PC处理器是4核,java version "1.8.0_231",例子中创建20个线程,每次测试循环500遍。

其实我也想测试更多的,但是一是慢,二是再多的数据会让Excel更卡。我做了N次测试,取了几次的数据,做成简易图表。

图中Y轴单位是纳秒,同时我删掉了部分数值特别大的,否则全挤到一块了。

首先是位移运算

第一次synchronized耗时3097800ns

第一次ReentrantLock耗时10469300ns

132-lock.png

第二次synchronized耗时1902600ns

132-sync2.png

第二次ReentrantLock耗时8310800ns

132-lock2.png

小结

synchronized有更好的稳定性和性能,更多点集中在底部,普遍低于500ns,数值高的点比Lock少很多。

##然后是写文件

第一次synchronized耗时97618604400ns

filesync1.png

第一次ReentrantLock耗时87893170600ns

filelock1.png

第二次synchronized耗时97205823500ns

filesync2.png

第二次ReentrantLock耗时89710725000ns

filelock2.png

小结

ReentrantLock性能相对稳定且更好,但是synchronized的点有很多点集中在底部,而ReentrantLock分散的均匀。

底部数据的比较

取第一次位运算的底部放大

synchronized

1sync.png

ReentrantLock

1lock.png

能看刚开始普遍耗时多,猜测是1.大量线程启动,2.在获得锁之前需要做运算,CPU繁忙,3竞争激烈。但是在这之后由synchronized做同步的操作都能在100~300之间完成。

我的总结

众所周知,synchronized由于偏向锁等优化性能有明显提高,所以现在单纯的说synchronized性能一定差就不一定准确了。

经过这几天我的反复测试,个人觉得synchronized在轻量化的操作,比如简单运算,变量递增/减,赋值等情况有更好的性能。ReentrantLock更适用于复杂度相对高的操作,比如循环遍历,插入,IO等。

在低并发,特别同时是轻量化的操作,synchronized可能可以获得更好的性能。现实情况中虽然我们可能有几百个线程,但是大多数情况下,对于共享资源的修改,同时只有几个或者十几个,那么使用synchronized也不失为一种好选择。

带来了更多的问题

例子中文件操作受IO干扰比较大,不合理,改成遍历长度100的数组并赋值会怎么样?(经简单测试,lock更快)

在线程重入的情况下两者性能如何呢?(位运算,遍历数组并赋值synchronized更快,高近3个数量级)

组合synchronized和volatile对变量的增减操作和直接使用Atomicxxxx哪个更快(本人PC上是前者快,特别是并发很小的时候)

这里不得再次提一下以上结果都是基于本人PC的测试数据。因为还有10核一台云主机,测试中发现对于本例中文件操作结果是不确定的,对于synchronized+volatile的例子是低并发比如5个线程,那是前者快,否则是后这快。

##后记

测试中的数据还可以继续挖掘,比如在某一区间内的分布等。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • ReentranReadWriteLock源码浅析

    c指的AbstractQueuedSynchronizer的state。SHARED_UNIT为65536。

    小金狗子
  • Java ClassLoader加载class过程

    关于class loader有太多太多的文章和图来讲过程。我就不多说了。以下是我认为的一些要点。

    小金狗子
  • 一简单线程同步笔试题分享,欢迎纠错分享更多思路

    有线程:worker1、worker2 ,work1只能累加奇数、work2累加偶数,

    小金狗子
  • 深信服一面C++

    牛客网
  • 那些有实力进入 BAT 的本科生,都做对了什么事?

    最近这段时间,我们部门来了几个年纪轻轻的本科生,最小的比我们小五岁左,这对于我来说还是比较有冲击力的。

    黄小斜
  • 手写vue框架(一)

    如图,but ,我们平时不是这样用_data的,so ,在这里我们需要一个代理proxy:

    用户3258338
  • on duplicate key update

    INSERT INTO issue_change(project_id, add_date, change_amount, base_line, gmt_cr...

    一个会写诗的程序员
  • 通过ASP.NET Web API + JQuery创建一个简单的Web应用

    看了dudu的《HttpClient + ASP.NET Web API, WCF之外的另一个选择》一文,想起多很久之前体现ASP.NET Web API而创建...

    蒋金楠
  • ts 单例模式

    AnRFDev
  • 产品经理最小技能指南之化繁为简的一页纸思考法

    在这个快节奏的时代,每天新概念、新名词、新思想应接不暇。每个人或多或少都存在焦虑感,年轻人焦虑未来、父母焦虑小孩、小孩焦虑考试。这人群中,创业者和企业家人群尤为...

    mixlab

扫码关注云+社区

领取腾讯云代金券