java?ReentrantLock并發(fā)鎖使用詳解
一、ReentrantLock是什么
ReentrantLock是一種基于AQS框架的應(yīng)用實(shí)現(xiàn),是JDK中的一種線程并發(fā)訪問(wèn)的同步手段,它的功能類似于synchronized是一種互斥鎖,可以保證線程安全。
相對(duì)于 synchronized, ReentrantLock具備如下特點(diǎn):
- 可中斷
- 可以設(shè)置超時(shí)時(shí)間
- 可以設(shè)置為公平鎖
- 支持多個(gè)條件變量
- 與 synchronized 一樣,都支持可重入
進(jìn)入源碼可以看到,其實(shí)現(xiàn)了公平鎖和非公平鎖

內(nèi)部實(shí)現(xiàn)了加鎖的操作,并且支持重入鎖。不用我們?cè)僦貙?/p>

解鎖操作

1-1、ReentrantLock和synchronized區(qū)別
synchronized和ReentrantLock的區(qū)別:
- synchronized是JVM層次的鎖實(shí)現(xiàn),ReentrantLock是JDK層次的鎖實(shí)現(xiàn);
- synchronized的鎖狀態(tài)是無(wú)法在代碼中直接判斷的,但是ReentrantLock可以通過(guò)ReentrantLock#isLocked判斷;
- synchronized是非公平鎖,ReentrantLock是可以是公平也可以是非公平的;
- synchronized是不可以被中斷的,而ReentrantLock#lockInterruptibly方法是可以被中斷的;
- 在發(fā)生異常時(shí)synchronized會(huì)自動(dòng)釋放鎖,而ReentrantLock需要開(kāi)發(fā)者在finally塊中顯示釋放鎖;
- ReentrantLock獲取鎖的形式有多種:如立即返回是否成功的tryLock(),以及等待指定時(shí)長(zhǎng)的獲取,更加靈活;
- synchronized在特定的情況下對(duì)于已經(jīng)在等待的線程是后來(lái)的線程先獲得鎖(回顧一下sychronized的喚醒策略),而ReentrantLock對(duì)于已經(jīng)在等待的線程是先來(lái)的線程先獲得鎖;
1-2、ReentrantLock的使用
1-2-1、ReentrantLock同步執(zhí)行,類似synchronized
使用ReentrantLock需要注意的是:一定要在finally中進(jìn)行解鎖,方式業(yè)務(wù)拋出異常,無(wú)法解鎖
public class ReentrantLockDemo {
private static int sum = 0;
private static ReentrantLock lock=new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(()->{
//加鎖
lock.lock();
try {
// 臨界區(qū)代碼
// TODO 業(yè)務(wù)邏輯:讀寫操作不能保證線程安全
for (int j = 0; j < 10000; j++) {
sum++;
}
} finally {
// 解鎖--一定要在finally中解鎖,防止業(yè)務(wù)代碼異常,無(wú)法釋放鎖
lock.unlock();
}
});
thread.start();
}
Thread.sleep(2000);
System.out.println(sum);
}
}
測(cè)試結(jié)果:

1-2-2、可重入鎖
可重入鎖就是 A(加鎖)-->調(diào)用--->B(加鎖)-->調(diào)用-->C(加鎖),從A到C即使B/C都有加鎖,也可以進(jìn)入
@Slf4j
public class ReentrantLockDemo2 {
public static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
method1();
}
public static void method1() {
lock.lock();
try {
log.debug("execute method1");
method2();
} finally {
lock.unlock();
}
}
public static void method2() {
lock.lock();
try {
log.debug("execute method2");
method3();
} finally {
lock.unlock();
}
}
public static void method3() {
lock.lock();
try {
log.debug("execute method3");
} finally {
lock.unlock();
}
}
}
執(zhí)行結(jié)果:

1-2-3、鎖中斷
可以使用lockInterruptibly來(lái)進(jìn)行鎖中斷
lockInterruptibly()方法能夠中斷等待獲取鎖的線程。當(dāng)兩個(gè)線程同時(shí)通過(guò)lock.lockInterruptibly()獲取某個(gè)鎖時(shí),假若此時(shí)線程A獲取到了鎖,而線程B只有等待,那么對(duì)線程B調(diào)用threadB.interrupt()方法能夠中斷線程B的等待過(guò)程。
public class ReentrantLockDemo3 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
log.debug("t1啟動(dòng)...");
try {
lock.lockInterruptibly();
try {
log.debug("t1獲得了鎖");
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
log.debug("t1等鎖的過(guò)程中被中斷");
}
}, "t1");
lock.lock();
try {
log.debug("main線程獲得了鎖");
t1.start();
//先讓線程t1執(zhí)行
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.interrupt();
log.debug("線程t1執(zhí)行中斷");
} finally {
lock.unlock();
}
}
}
執(zhí)行結(jié)果:

1-2-4、獲得鎖超時(shí)失敗
可以讓線程等待指定的時(shí)間,如果還未獲取鎖則進(jìn)行失敗處理。
如下代碼,首先讓主線程獲得鎖,然后讓子線程啟動(dòng)嘗試獲取鎖,但是由于主線程獲取鎖之后,讓線程等待了2秒,而子線程獲得鎖的超時(shí)時(shí)間只有1秒,如果未獲得鎖,則進(jìn)行return失敗處理
public class ReentrantLockDemo4 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
log.debug("t1啟動(dòng)...");
//超時(shí)
try {
if (!lock.tryLock(1, TimeUnit.SECONDS)) {
log.debug("等待 1s 后獲取鎖失敗,返回");
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
try {
log.debug("t1獲得了鎖");
} finally {
lock.unlock();
}
}, "t1");
lock.lock();
try {
log.debug("main線程獲得了鎖");
t1.start();
//先讓線程t1執(zhí)行
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
lock.unlock();
}
}
}
執(zhí)行結(jié)果:

1-2-5、公平鎖
ReentrantLock 默認(rèn)是不公平的

首先啟動(dòng)500次for循環(huán)創(chuàng)建500個(gè)線程,然后進(jìn)行加鎖操作,并同時(shí)啟動(dòng)了。這樣這500個(gè)線程就依次排隊(duì)等待加鎖的處理
下面500個(gè)線程也是等待加鎖操作
如果使用公平鎖,下面500的線程只有等上面500個(gè)線程運(yùn)行完成之后才能獲得鎖。
@Slf4j
public class ReentrantLockDemo5 {
public static void main(String[] args) throws InterruptedException {
ReentrantLock lock = new ReentrantLock(true); //公平鎖
for (int i = 0; i < 500; i++) {
new Thread(() -> {
lock.lock();
try {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug(Thread.currentThread().getName() + " running...");
} finally {
lock.unlock();
}
}, "t" + i).start();
}
// 1s 之后去爭(zhēng)搶鎖
Thread.sleep(1000);
for (int i = 0; i < 500; i++) {
new Thread(() -> {
lock.lock();
try {
log.debug(Thread.currentThread().getName() + " running...");
} finally {
lock.unlock();
}
}, "強(qiáng)行插入" + i).start();
}
}
}
測(cè)試結(jié)果(后進(jìn)入的線程都在等待排隊(duì))

使用非公平鎖的情況下,就可以看到下面500線程有些線程就可以搶占鎖了

那ReentrantLock為什么默認(rèn)使用非公平鎖呢?實(shí)際上就是為了提高性能,如果使用公平鎖,當(dāng)前鎖對(duì)象釋放之后,還需要去隊(duì)列中獲取第一個(gè)排隊(duì)的線程,然后進(jìn)行加鎖處理。而非公平鎖,可能再當(dāng)前對(duì)象釋放鎖之后,正好有新的線程在獲取鎖,這樣就可以直接進(jìn)行加鎖操作,不必再去隊(duì)列中讀取。
以上就是java ReentrantLock并發(fā)鎖使用詳解的詳細(xì)內(nèi)容,更多關(guān)于java ReentrantLock并發(fā)鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaGUI界面實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)方法
這篇文章主要給大家介紹了關(guān)于JavaGUI界面實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)的相關(guān)資料, GUI是指圖形用戶界面,指采用圖形方式顯示的計(jì)算機(jī)操作用戶界面,需要的朋友可以參考下2023-07-07
在java中使用SPI創(chuàng)建可擴(kuò)展的應(yīng)用程序操作
這篇文章主要介紹了在java中使用SPI創(chuàng)建可擴(kuò)展的應(yīng)用程序操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09
java實(shí)現(xiàn)gif動(dòng)畫效果(java顯示動(dòng)態(tài)圖片)
這篇文章主要介紹了java實(shí)現(xiàn)gif動(dòng)畫效果示例(java顯示動(dòng)態(tài)圖片),需要的朋友可以參考下2014-04-04
基于SpringCloud手寫一個(gè)簡(jiǎn)易版Sentinel
SpringCloud Alibaba Sentinel是當(dāng)前最為流行一種熔斷降級(jí)框架,簡(jiǎn)單易用的方式可以快速幫助我們實(shí)現(xiàn)服務(wù)的限流和降級(jí),保證服務(wù)的穩(wěn)定性。2021-05-05
springBoot?啟動(dòng)指定配置文件環(huán)境多種方案(最新推薦)
springBoot?啟動(dòng)指定配置文件環(huán)境理論上是有多種方案的,一般都是結(jié)合我們的實(shí)際業(yè)務(wù)選擇不同的方案,比如,有pom.xml文件指定、maven命令行指定、配置文件指定、啟動(dòng)jar包時(shí)指定等方案,今天我們一一分享一下,需要的朋友可以參考下2023-09-09
SpringBoot整合POI實(shí)現(xiàn)Excel文件讀寫操作
EasyExcel是一個(gè)基于Java的、快速、簡(jiǎn)潔、解決大文件內(nèi)存溢出的Excel處理工具,這篇文章主要介紹了SpringBoot整合POI實(shí)現(xiàn)Excel文件讀寫操作,首先準(zhǔn)備環(huán)境進(jìn)行一系列操作,本文給大家介紹的非常詳細(xì),需要的朋友參考下吧2023-10-10

