您好,登錄后才能下訂單哦!
(1)ReentrantLock有哪些優(yōu)點(diǎn)?
(2)ReentrantLock有哪些缺點(diǎn)?
(3)ReentrantLock是否可以完全替代synchronized?
synchronized是Java原生提供的用于在多線程環(huán)境中保證同步的關(guān)鍵字,底層是通過(guò)修改對(duì)象頭中的MarkWord來(lái)實(shí)現(xiàn)的。
ReentrantLock是Java語(yǔ)言層面提供的用于在多線程環(huán)境中保證同步的類,底層是通過(guò)原子更新?tīng)顟B(tài)變量state來(lái)實(shí)現(xiàn)的。
既然有了synchronized的關(guān)鍵字來(lái)保證同步了,為什么還要實(shí)現(xiàn)一個(gè)ReentrantLock類呢?它們之間有什么異同呢?
直接上表格:(手機(jī)橫屏查看更方便)
功能 | ReentrantLock | synchronized |
---|---|---|
可重入 | 支持 | 支持 |
非公平 | 支持(默認(rèn)) | 支持 |
加鎖/解鎖方式 | 需要手動(dòng)加鎖、解鎖,一般使用try..finally..保證鎖能夠釋放 | 手動(dòng)加鎖,無(wú)需刻意解鎖 |
按key鎖 | 不支持,比如按用戶id加鎖 | 支持,synchronized加鎖時(shí)需要傳入一個(gè)對(duì)象 |
公平鎖 | 支持,new ReentrantLock(true) | 不支持 |
中斷 | 支持,lockInterruptibly() | 不支持 |
嘗試加鎖 | 支持,tryLock() | 不支持 |
超時(shí)鎖 | 支持,tryLock(timeout, unit) | 不支持 |
獲取當(dāng)前線程獲取鎖的次數(shù) | 支持,getHoldCount() | 不支持 |
獲取等待的線程 | 支持,getWaitingThreads() | 不支持 |
檢測(cè)是否被當(dāng)前線程占有 | 支持,isHeldByCurrentThread() | 不支持 |
檢測(cè)是否被任意線程占有 | 支持,isLocked() | 不支持 |
條件鎖 | 可支持多個(gè)條件,condition.await(),condition.signal(),condition.signalAll() | 只支持一個(gè),obj.wait(),obj.notify(),obj.notifyAll() |
在測(cè)試之前,我們先預(yù)想一下結(jié)果,隨著線程數(shù)的不斷增加,ReentrantLock(fair)、ReentrantLock(unfair)、synchronized三者的效率怎樣呢?
我猜測(cè)應(yīng)該是ReentrantLock(unfair)> synchronized > ReentrantLock(fair)。
到底是不是這樣呢?
直接上測(cè)試代碼:(為了全面對(duì)比,彤哥這里把AtomicInteger和LongAdder也拿來(lái)一起對(duì)比了)
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));
}
}
運(yùn)行這段代碼,你會(huì)發(fā)現(xiàn)結(jié)果大大出乎意料,真的是不測(cè)不知道,一測(cè)嚇一跳,運(yùn)行后發(fā)現(xiàn)以下規(guī)律:
隨著線程數(shù)的不斷增加,synchronized的效率竟然比ReentrantLock非公平模式要高!
彤哥的電腦上大概是高3倍左右,我的運(yùn)行環(huán)境是4核8G,java版本是8,請(qǐng)大家一定要在自己電腦上運(yùn)行一下,并且最好能給我反饋一下。
彤哥又使用Java7及以下的版本運(yùn)行了,發(fā)現(xiàn)在Java7及以下版本中synchronized的效率確實(shí)比ReentrantLock的效率低一些。
(1)synchronized是Java原生關(guān)鍵字鎖;
(2)ReentrantLock是Java語(yǔ)言層面提供的鎖;
(3)ReentrantLock的功能非常豐富,解決了很多synchronized的局限性;
(4)至于在非公平模式下,ReentrantLock與synchronized的效率孰高孰低,彤哥給出的結(jié)論是隨著Java版本的不斷升級(jí),synchronized的效率只會(huì)越來(lái)越高;
既然ReentrantLock的功能更豐富,而且效率也不低,我們是不是可以放棄使用synchronized了呢?
答:我認(rèn)為不是。因?yàn)閟ynchronized是Java原生支持的,隨著Java版本的不斷升級(jí),Java團(tuán)隊(duì)也是在不斷優(yōu)化synchronized,所以我認(rèn)為在功能相同的前提下,最好還是使用原生的synchronized關(guān)鍵字來(lái)加鎖,這樣我們就能獲得Java版本升級(jí)帶來(lái)的免費(fèi)的性能提升的空間。
另外,在Java8的ConcurrentHashMap中已經(jīng)把ReentrantLock換成了synchronized來(lái)分段加鎖了,這也是Java版本不斷升級(jí)帶來(lái)的免費(fèi)的synchronized的性能提升。
死磕 java同步系列之ReentrantLock源碼解析(二)——條件鎖
死磕 java同步系列之ReentrantLock源碼解析(一)——公平鎖、非公平鎖
死磕 java同步系列之AQS起篇
死磕 java同步系列之自己動(dòng)手寫一個(gè)鎖Lock
死磕 java魔法類之Unsafe解析
死磕 java同步系列之JMM(Java Memory Model)
死磕 java同步系列之volatile解析
歡迎關(guān)注我的公眾號(hào)“彤哥讀源碼”,查看更多源碼系列文章, 與彤哥一起暢游源碼的海洋。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。