重入锁:ReentrantLock 详解
在JDK5.0版本之前,重入鎖的性能遠(yuǎn)遠(yuǎn)好于synchronized關(guān)鍵字,JDK6.0版本之后synchronized 得到了大量的優(yōu)化,二者性能也不分伯仲,但是重入鎖是可以完全替代synchronized關(guān)鍵字的。除此之外,重入鎖又自帶一系列高逼格UBFF:可中斷響應(yīng)、鎖申請(qǐng)等待限時(shí)、公平鎖。另外可以結(jié)合Condition來(lái)使用,使其更是逼格滿滿。
先來(lái)盤花生米:
package somhu;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockTest implements Runnable{
? ? public static ReentrantLock lock = new ReentrantLock();
? ? public static int i = 0;
? ? @Override
? ? public void run() {
? ? ? ? for (int j = 0; j < 10000; j++) {
? ? ? ? ? ? lock.lock(); ?// 看這里就可以
? ? ? ? ? ? //lock.lock(); ①
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? i++;
? ? ? ? ? ? } finally {
? ? ? ? ? ? ? ? lock.unlock(); // 看這里就可以
? ? ? ? ? ? ? ? //lock.unlock();②
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? public static void main(String[] args) throws InterruptedException {
? ? ? ? ReentrantLockTest test = new ReentrantLockTest();
? ? ? ? Thread t1 = new Thread(test);
? ? ? ? Thread t2 = new Thread(test);
? ? ? ? t1.start();t2.start();
? ? ? ? t1.join(); t2.join(); // main線程會(huì)等待t1和t2都運(yùn)行完再執(zhí)行以后的流程
? ? ? ? System.err.println(i);
? ? }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
從上可以看出,使用重入鎖進(jìn)行加鎖是一種顯式操作,通過(guò)何時(shí)加鎖與釋放鎖使重入鎖對(duì)邏輯控制的靈活性遠(yuǎn)遠(yuǎn)大于synchronized關(guān)鍵字。同時(shí),需要注意,有加鎖就必須有釋放鎖,而且加鎖與釋放鎖的分?jǐn)?shù)要相同,這里就引出了“重”字的概念,如上邊代碼演示,放開(kāi)①、②處的注釋,與原來(lái)效果一致。
硬菜來(lái)了:
1、中斷響應(yīng)
對(duì)于synchronized塊來(lái)說(shuō),要么獲取到鎖執(zhí)行,要么持續(xù)等待。而重入鎖的中斷響應(yīng)功能就合理地避免了這樣的情況。比如,一個(gè)正在等待獲取鎖的線程被“告知”無(wú)須繼續(xù)等待下去,就可以停止工作了。直接上代碼,來(lái)演示使用重入鎖如何解決死鎖:
1
package somhu;
import java.util.concurrent.locks.ReentrantLock;
public class KillDeadlock implements Runnable{
? ? public static ReentrantLock lock1 = new ReentrantLock();
? ? public static ReentrantLock lock2 = new ReentrantLock();
? ? int lock;
? ? public KillDeadlock(int lock) {
? ? ? ? this.lock = lock;
? ? }
? ? @Override
? ? public void run() {
? ? ? ? try {
? ? ? ? ? ? if (lock == 1) {
? ? ? ? ? ? ? ? lock1.lockInterruptibly(); ?// 以可以響應(yīng)中斷的方式加鎖
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? Thread.sleep(500);
? ? ? ? ? ? ? ? } catch (InterruptedException e) {}
? ? ? ? ? ? ? ? lock2.lockInterruptibly();
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? lock2.lockInterruptibly(); ?// 以可以響應(yīng)中斷的方式加鎖
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? Thread.sleep(500);
? ? ? ? ? ? ? ? } catch (InterruptedException e) {}
? ? ? ? ? ? ? ? lock1.lockInterruptibly();
? ? ? ? ? ? }
? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } finally {
? ? ? ? ? ? if (lock1.isHeldByCurrentThread()) lock1.unlock(); ?// 注意判斷方式
? ? ? ? ? ? if (lock2.isHeldByCurrentThread()) lock2.unlock();
? ? ? ? ? ? System.err.println(Thread.currentThread().getId() + "退出!");
? ? ? ? }
? ? }
? ? public static void main(String[] args) throws InterruptedException {
? ? ? ? KillDeadlock deadLock1 = new KillDeadlock(1);
? ? ? ? KillDeadlock deadLock2 = new KillDeadlock(2);
? ? ? ? Thread t1 = new Thread(deadLock1);
? ? ? ? Thread t2 = new Thread(deadLock2);
? ? ? ? t1.start();t2.start();
? ? ? ? Thread.sleep(1000);
? ? ? ? t2.interrupt(); // ③
? ? }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
t1、t2線程開(kāi)始運(yùn)行時(shí),會(huì)分別持有l(wèi)ock1和lock2而請(qǐng)求lock2和lock1,這樣就發(fā)生了死鎖。但是,在③處給t2線程狀態(tài)標(biāo)記為中斷后,持有重入鎖lock2的線程t2會(huì)響應(yīng)中斷,并不再繼續(xù)等待lock1,同時(shí)釋放了其原本持有的lock2,這樣t1獲取到了lock2,正常執(zhí)行完成。t2也會(huì)退出,但只是釋放了資源并沒(méi)有完成工作。
2、鎖申請(qǐng)等待限時(shí)
可以使用 tryLock()或者tryLock(long timeout, TimeUtil unit) 方法進(jìn)行一次限時(shí)的鎖等待。
前者不帶參數(shù),這時(shí)線程嘗試獲取鎖,如果獲取到鎖則繼續(xù)執(zhí)行,如果鎖被其他線程持有,則立即返回 false ,也就是不會(huì)使當(dāng)前線程等待,所以不會(huì)產(chǎn)生死鎖。?
后者帶有參數(shù),表示在指定時(shí)長(zhǎng)內(nèi)獲取到鎖則繼續(xù)執(zhí)行,如果等待指定時(shí)長(zhǎng)后還沒(méi)有獲取到鎖則返回false。
上代碼:
package somhu;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class TryLockTest implements Runnable{
? ? public static ReentrantLock lock = new ReentrantLock();
? ? @Override
? ? public void run() {
? ? ? ? try {
? ? ? ? ? ? if (lock.tryLock(1, TimeUnit.SECONDS)) { // 等待1秒
? ? ? ? ? ? ? ? Thread.sleep(2000); ?//休眠2秒
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? System.err.println(Thread.currentThread().getName() + "獲取鎖失敗!");
? ? ? ? ? ? }
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? if (lock.isHeldByCurrentThread()) lock.unlock();
? ? ? ? }
? ? }
? ? public static void main(String[] args) throws InterruptedException {
? ? ? ? TryLockTest test = new TryLockTest();
? ? ? ? Thread t1 = new Thread(test); t1.setName("線程1");
? ? ? ? Thread t2 = new Thread(test); t1.setName("線程2");
? ? ? ? t1.start();t2.start();
? ? }
}
/**
?* 運(yùn)行結(jié)果:
?* 線程2獲取鎖失敗!
?*/?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
上述示例中,t1先獲取到鎖,并休眠2秒,這時(shí)t2開(kāi)始等待,等待1秒后依然沒(méi)有獲取到鎖,就不再繼續(xù)等待,符合預(yù)期結(jié)果。
3、公平鎖
所謂公平鎖,就是按照時(shí)間先后順序,使先等待的線程先得到鎖,而且,公平鎖不會(huì)產(chǎn)生饑餓鎖,也就是只要排隊(duì)等待,最終能等待到獲取鎖的機(jī)會(huì)。使用重入鎖(默認(rèn)是非公平鎖)創(chuàng)建公平鎖:
public ReentrantLock(boolean fair) {
? ? sync = fair ? new FairSync() : new NonfairSync();
}
1
2
3
上代碼:
package somhu;
import java.util.concurrent.locks.ReentrantLock;
public class FairLockTest implements Runnable{
? ? public static ReentrantLock lock = new ReentrantLock(true);
? ? @Override
? ? public void run() {
? ? ? ? while (true) {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? lock.lock();
? ? ? ? ? ? ? ? System.err.println(Thread.currentThread().getName() + "獲取到了鎖!");
? ? ? ? ? ? } finally {
? ? ? ? ? ? ? ? lock.unlock();
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? public static void main(String[] args) throws InterruptedException {
? ? ? ? FairLockTest test = new FairLockTest();
? ? ? ? Thread t1 = new Thread(test, "線程1");
? ? ? ? Thread t2 = new Thread(test, "線程2");
? ? ? ? t1.start();t2.start();
? ? }
}
/**
?* 運(yùn)行結(jié)果:
?* 線程1獲取到了鎖!
?* 線程2獲取到了鎖!
?* 線程1獲取到了鎖!
?* 線程2獲取到了鎖!
?* 線程1獲取到了鎖!
?* 線程2獲取到了鎖!
?* 線程1獲取到了鎖!
?* 線程2獲取到了鎖!
?* 線程1獲取到了鎖!
?* 線程2獲取到了鎖!
?* 線程1獲取到了鎖!
?* 線程2獲取到了鎖!
?* 線程1獲取到了鎖!
?* 線程2獲取到了鎖!
?* 線程1獲取到了鎖!
?* 線程2獲取到了鎖!
?* ......(上邊是截取的一段)
?*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
可以發(fā)現(xiàn),t1和t2交替獲取到鎖。如果是非公平鎖,會(huì)發(fā)生t1運(yùn)行了許多遍后t2才開(kāi)始運(yùn)行的情況。
ReentrantLock 配合 Conditond 使用
配合關(guān)鍵字synchronized使用的方法如:await()、notify()、notifyAll(),同樣配合ReentrantLock 使用的Conditon提供了以下方法:
public interface Condition {
? ? void await() throws InterruptedException; // 類似于Object.wait()
? ? void awaitUninterruptibly(); // 與await()相同,但不會(huì)再等待過(guò)程中響應(yīng)中斷
? ? long awaitNanos(long nanosTimeout) throws InterruptedException;
? ? boolean await(long time, TimeUnit unit) throws InterruptedException;
? ? boolean awaitUntil(Date deadline) throws InterruptedException;
? ? void signal(); // 類似于Obejct.notify()
? ? void signalAll();
}
1
2
3
4
5
6
7
8
9
ReentrantLock 實(shí)現(xiàn)了Lock接口,可以通過(guò)該接口提供的newCondition()方法創(chuàng)建Condition對(duì)象:
public interface Lock {
? ? void lock();
? ? void lockInterruptibly() throws InterruptedException;
? ? boolean tryLock();
? ? boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
? ? void unlock();
? ? Condition newCondition();
}
1
2
3
4
5
6
7
8
上代碼:
package somhu;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockWithConditon implements Runnable{
? ? public static ReentrantLock lock = new ReentrantLock(true);
? ? public static Condition condition = lock.newCondition();
? ? @Override
? ? public void run() {
? ? ? ? lock.newCondition();
? ? ? ? try {
? ? ? ? ? ? lock.lock();
? ? ? ? ? ? System.err.println(Thread.currentThread().getName() + "-線程開(kāi)始等待...");
? ? ? ? ? ? condition.await();
? ? ? ? ? ? System.err.println(Thread.currentThread().getName() + "-線程繼續(xù)進(jìn)行了");
? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } finally {
? ? ? ? ? ? lock.unlock();
? ? ? ? }
? ? }
? ? public static void main(String[] args) throws InterruptedException {
? ? ? ? ReentrantLockWithConditon test = new ReentrantLockWithConditon();
? ? ? ? Thread t = new Thread(test, "線程ABC");
? ? ? ? t.start();
? ? ? ? Thread.sleep(1000);
? ? ? ? System.err.println("過(guò)了1秒后...");
? ? ? ? lock.lock();
? ? ? ? condition.signal(); // 調(diào)用該方法前需要獲取到創(chuàng)建該對(duì)象的鎖否則會(huì)產(chǎn)生
? ? ? ? ? ? ? ? ? ? ? ? ? ? // java.lang.IllegalMonitorStateException異常
? ? ? ? lock.unlock();
? ? }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
好了,到這里重入鎖ReentrantLock的基本使用方法就介紹完成了!
---------------------?
作者:Somhu?
來(lái)源:CSDN?
原文:https://blog.csdn.net/Somhu/article/details/78874634?
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請(qǐng)附上博文鏈接!
總結(jié)
以上是生活随笔為你收集整理的重入锁:ReentrantLock 详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java多线程系列(四)---Reent
- 下一篇: 深入理解ReentrantLock