ReentrantLock Fair 与 Unfair 的巨大差异

ReentrantLock  可重入的锁是我们平常除了intrinsic  lock  (也就是 synchronized 方法, synchronized block)之外用得最多的了同步方式了。 一般情况下 我们用 ReentrantLock  的时候就是用它的默认建构函数方式 

     new ReentrantLock  ();

但其实它带一个 参数 是否 fair。如果是true  也就是FairSync 所在有多个线程同时竞争这个锁得时候, 会考虑公平性尽可能的让不同的线程公平。 这个公平其实是有很大的性能损失换来的。下面有个例子 :  \


package com.joyreach.log.test;

import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestLocks implements Runnable  {
	
	public enum LockType { JVM, JUC }  
    public static LockType lockType;  
  
    public static final long ITERATIONS = 5L * 1000L * 1000L;  
    public static long counter = 0L;  
  
    public static final Object jvmLock = new Object();  
    public static final Lock jucLock = new ReentrantLock(false);  
    private static int numThreads;  
  
    private final long iterationLimit;  
    private final CyclicBarrier barrier;  
    private long localCounter = 0L;  
    public long getLocalCounter()   
    {  
        return localCounter;  
    }   
  
    public TestLocks(final CyclicBarrier barrier, final long iterationLimit)  
    {  
        this.barrier = barrier;  
        this.iterationLimit = iterationLimit;  
    }  
  
    public static void main(final String[] args) throws Exception  
    {  
        lockType = LockType.valueOf("JUC");  
        numThreads = Integer.parseInt("8");  
  
        final long start = System.nanoTime();  
        runTest(numThreads, ITERATIONS);  
        final long duration = System.nanoTime() - start;  
  
        /*out.printf("%d threads, duration %,d (ns)\n", numThreads, duration);  
        out.printf("%,d ns/op\n", duration / ITERATIONS);  
        out.printf("%,d ops/s\n", (ITERATIONS * 1000000000L) / duration);  
        out.println("counter = " + counter);  */
    }  
  
    private static void runTest(final int numThreads, final long iterationLimit)  
        throws Exception  
    {  
        CyclicBarrier barrier = new CyclicBarrier(numThreads);  
        Thread[] threads = new Thread[numThreads];  
        TestLocks[] testLocks = new TestLocks[numThreads];  
        for (int i = 0; i < threads.length; i++)  
        {  
            testLocks[i] = new TestLocks(barrier, iterationLimit);  
            threads[i] = new Thread(testLocks[i]);  
        }  
  
        for (Thread t : threads)  
        {  
            t.start();  
        }  
  
        for (Thread t : threads)  
        {  
            t.join();  
        }  
        for (int i = 0; i < threads.length; i++)  
        {  
            System.out.println(i+" thread, local counter = "+ testLocks[i].getLocalCounter());  
        }  
    }  
  
    public void run()  
    {  
        try  
        {  
            barrier.await();  
        }  
        catch (Exception e)  
        {  
            // don't care  
        }  
  
        switch (lockType)  
        {  
            case JVM: jvmLockInc(); break;  
            case JUC: jucLockInc(); break;  
        }  
    }  
  
    private void jvmLockInc()  
    {  
          
        while (true)  
        {  
            long count = 0;  
            synchronized (jvmLock)  
            {  
                ++counter;  
                count = counter;  
            }  
            localCounter++;  
            if (count >= iterationLimit)  {  
                break;  
            }  
        }  
    }  
  
    private void jucLockInc()  
    {  
        while (true)  
        {  
            long count = 0L;  
            jucLock.lock();  
            try  
            {  
                ++counter;  
                count = counter;  
            }  
            finally  
            {  
                jucLock.unlock();  
            }  
            localCounter++;  
            if (count >= iterationLimit)  {  
                break;  
            }  
        }  
    }  

}

我们简单用N个线程来同步一个counter  5,000,000次。 如果是 new ReentrantLock(true) 也就是 FairSync 方式 :

 0 thread, local counter = 624,822

1 thread, local counter = 625,135

2 thread, local counter = 624,936

3 thread, local counter = 624,800

4 thread, local counter = 625,007

5 thread, local counter = 624,921

6 thread, local counter = 625,298

7 thread, local counter = 625,088

8 threads, duration 16,553,236,994 (ns)

3,310 ns/op

302,055 ops/s

counter = 5000007

 

 

可以看到8 个线程 每个线程的获取lock都很接近 但是它要 3310 个ns 来进行一次。   如果采用  如果是 new ReentrantLock(false) 就是 UnfairSync 方式:

0 thread, local counter = 626,786

1 thread, local counter = 594,983

2 thread, local counter = 590,274

3 thread, local counter = 688,725

4 thread, local counter = 588,090

5 thread, local counter = 586,885

6 thread, local counter = 732,210

7 thread, local counter = 592,054

8 threads, duration 425,844,254 (ns)

85 ns/op

11,741,381 ops/s

counter = 5000007

虽然 每个thread 获取lock 的次数差异很大 从   592,054到  732,210,  但每次操作自需要 85 ns。  3310 对 85 这个差异太大聊。  

如果我们用intrinsic lock 即(synchronized) 的方法 结果如下:

 

0 thread, local counter = 498,363

1 thread, local counter = 512,603

2 thread, local counter = 799,367

3 thread, local counter = 500,946

4 thread, local counter = 824,935

5 thread, local counter = 652,921

6 thread, local counter = 692,219

7 thread, local counter = 518,653

8 threads, duration 877,777,848 (ns)

175 ns/op

5,696,202 ops/s

counter = 5000007

 

intrinsic lock 也应该是unfair 的方式, 每个线程获取的机会差异比较大, 每个操作需要 175ns。  比 unfair 的 ReentrantLock  性能差些。   

 得出的结果是 如果我们仅考虑同步锁得性能不需要考虑公平性优先考虑 

 new ReentrantLock(false)    

 再次是 intrinsic lock(synchronized)

万不得已的必须要FairSync 的情况下才用 new ReentrantLock(true)。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值