(1)ReentrantLock有哪些優勢?java
(2)ReentrantLock有哪些缺點?c++
(3)ReentrantLock是否能夠徹底替代synchronized?多線程
synchronized是Java原生提供的用於在多線程環境中保證同步的關鍵字,底層是經過修改對象頭中的MarkWord來實現的。oop
ReentrantLock是Java語言層面提供的用於在多線程環境中保證同步的類,底層是經過原子更新狀態變量state來實現的。性能
既然有了synchronized的關鍵字來保證同步了,爲何還要實現一個ReentrantLock類呢?它們之間有什麼異同呢?測試
直接上表格:(手機橫屏查看更方便)優化
功能 | ReentrantLock | synchronized |
---|---|---|
可重入 | 支持 | 支持 |
非公平 | 支持(默認) | 支持 |
加鎖/解鎖方式 | 須要手動加鎖、解鎖,通常使用try..finally..保證鎖可以釋放 | 手動加鎖,無需刻意解鎖 |
按key鎖 | 不支持,好比按用戶id加鎖 | 支持,synchronized加鎖時須要傳入一個對象 |
公平鎖 | 支持,new ReentrantLock(true) | 不支持 |
中斷 | 支持,lockInterruptibly() | 不支持 |
嘗試加鎖 | 支持,tryLock() | 不支持 |
超時鎖 | 支持,tryLock(timeout, unit) | 不支持 |
獲取當前線程獲取鎖的次數 | 支持,getHoldCount() | 不支持 |
獲取等待的線程 | 支持,getWaitingThreads() | 不支持 |
檢測是否被當前線程佔有 | 支持,isHeldByCurrentThread() | 不支持 |
檢測是否被任意線程佔有 | 支持,isLocked() | 不支持 |
條件鎖 | 可支持多個條件,condition.await(),condition.signal(),condition.signalAll() | 只支持一個,obj.wait(),obj.notify(),obj.notifyAll() |
在測試以前,咱們先預想一下結果,隨着線程數的不斷增長,ReentrantLock(fair)、ReentrantLock(unfair)、synchronized三者的效率怎樣呢?線程
我猜想應該是ReentrantLock(unfair)> synchronized > ReentrantLock(fair)。code
究竟是不是這樣呢?對象
直接上測試代碼:(爲了全面對比,彤哥這裏把AtomicInteger和LongAdder也拿來一塊兒對比了)
public class ReentrantLockVsSynchronizedTest { public static AtomicInteger a = new AtomicInteger(0); public static LongAdder b = new LongAdder(); public static int c = 0; public static int d = 0; public static int e = 0; public static final ReentrantLock fairLock = new ReentrantLock(true); public static final ReentrantLock unfairLock = new ReentrantLock(); public static void main(String[] args) throws InterruptedException { System.out.println("-------------------------------------"); testAll(1, 100000); System.out.println("-------------------------------------"); testAll(2, 100000); System.out.println("-------------------------------------"); testAll(4, 100000); System.out.println("-------------------------------------"); testAll(6, 100000); System.out.println("-------------------------------------"); testAll(8, 100000); System.out.println("-------------------------------------"); testAll(10, 100000); System.out.println("-------------------------------------"); testAll(50, 100000); System.out.println("-------------------------------------"); testAll(100, 100000); System.out.println("-------------------------------------"); testAll(200, 100000); System.out.println("-------------------------------------"); testAll(500, 100000); System.out.println("-------------------------------------"); // testAll(1000, 1000000); System.out.println("-------------------------------------"); testAll(500, 10000); System.out.println("-------------------------------------"); testAll(500, 1000); System.out.println("-------------------------------------"); testAll(500, 100); System.out.println("-------------------------------------"); testAll(500, 10); System.out.println("-------------------------------------"); testAll(500, 1); System.out.println("-------------------------------------"); } public static void testAll(int threadCount, int loopCount) throws InterruptedException { testAtomicInteger(threadCount, loopCount); testLongAdder(threadCount, loopCount); testSynchronized(threadCount, loopCount); testReentrantLockUnfair(threadCount, loopCount); // testReentrantLockFair(threadCount, loopCount); } public static void testAtomicInteger(int threadCount, int loopCount) throws InterruptedException { long start = System.currentTimeMillis(); CountDownLatch countDownLatch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { new Thread(() -> { for (int j = 0; j < loopCount; j++) { a.incrementAndGet(); } countDownLatch.countDown(); }).start(); } countDownLatch.await(); System.out.println("testAtomicInteger: result=" + a.get() + ", threadCount=" + threadCount + ", loopCount=" + loopCount + ", elapse=" + (System.currentTimeMillis() - start)); } public static void testLongAdder(int threadCount, int loopCount) throws InterruptedException { long start = System.currentTimeMillis(); CountDownLatch countDownLatch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { new Thread(() -> { for (int j = 0; j < loopCount; j++) { b.increment(); } countDownLatch.countDown(); }).start(); } countDownLatch.await(); System.out.println("testLongAdder: result=" + b.sum() + ", threadCount=" + threadCount + ", loopCount=" + loopCount + ", elapse=" + (System.currentTimeMillis() - start)); } public static void testReentrantLockFair(int threadCount, int loopCount) throws InterruptedException { long start = System.currentTimeMillis(); CountDownLatch countDownLatch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { new Thread(() -> { for (int j = 0; j < loopCount; j++) { fairLock.lock(); // 消除try的性能影響 // try { c++; // } finally { fairLock.unlock(); // } } countDownLatch.countDown(); }).start(); } countDownLatch.await(); System.out.println("testReentrantLockFair: result=" + c + ", threadCount=" + threadCount + ", loopCount=" + loopCount + ", elapse=" + (System.currentTimeMillis() - start)); } public static void testReentrantLockUnfair(int threadCount, int loopCount) throws InterruptedException { long start = System.currentTimeMillis(); CountDownLatch countDownLatch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { new Thread(() -> { for (int j = 0; j < loopCount; j++) { unfairLock.lock(); // 消除try的性能影響 // try { d++; // } finally { unfairLock.unlock(); // } } countDownLatch.countDown(); }).start(); } countDownLatch.await(); System.out.println("testReentrantLockUnfair: result=" + d + ", threadCount=" + threadCount + ", loopCount=" + loopCount + ", elapse=" + (System.currentTimeMillis() - start)); } public static void testSynchronized(int threadCount, int loopCount) throws InterruptedException { long start = System.currentTimeMillis(); CountDownLatch countDownLatch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { new Thread(() -> { for (int j = 0; j < loopCount; j++) { synchronized (ReentrantLockVsSynchronizedTest.class) { e++; } } countDownLatch.countDown(); }).start(); } countDownLatch.await(); System.out.println("testSynchronized: result=" + e + ", threadCount=" + threadCount + ", loopCount=" + loopCount + ", elapse=" + (System.currentTimeMillis() - start)); } }
運行這段代碼,你會發現結果大大出乎意料,真的是不測不知道,一測嚇一跳,運行後發現如下規律:
隨着線程數的不斷增長,synchronized的效率居然比ReentrantLock非公平模式要高!
彤哥的電腦上大概是高3倍左右,個人運行環境是4核8G,java版本是8,請你們必定要在本身電腦上運行一下,而且最好能給我反饋一下。
彤哥又使用Java7及如下的版本運行了,發如今Java7及如下版本中synchronized的效率確實比ReentrantLock的效率低一些。
(1)synchronized是Java原生關鍵字鎖;
(2)ReentrantLock是Java語言層面提供的鎖;
(3)ReentrantLock的功能很是豐富,解決了不少synchronized的侷限性;
(4)至於在非公平模式下,ReentrantLock與synchronized的效率孰高孰低,彤哥給出的結論是隨着Java版本的不斷升級,synchronized的效率只會愈來愈高;
既然ReentrantLock的功能更豐富,並且效率也不低,咱們是否是能夠放棄使用synchronized了呢?
答:我認爲不是。由於synchronized是Java原生支持的,隨着Java版本的不斷升級,Java團隊也是在不斷優化synchronized,因此我認爲在功能相同的前提下,最好仍是使用原生的synchronized關鍵字來加鎖,這樣咱們就能得到Java版本升級帶來的免費的性能提高的空間。
另外,在Java8的ConcurrentHashMap中已經把ReentrantLock換成了synchronized來分段加鎖了,這也是Java版本不斷升級帶來的免費的synchronized的性能提高。
歡迎關注個人公衆號「彤哥讀源碼」,查看更多源碼系列文章, 與彤哥一塊兒暢遊源碼的海洋。