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

內(nèi)部實現(xiàn)了加鎖的操作,并且支持重入鎖。不用我們再重寫

解鎖操作

1-1、ReentrantLock和synchronized區(qū)別
synchronized和ReentrantLock的區(qū)別:
- synchronized是JVM層次的鎖實現(xiàn),ReentrantLock是JDK層次的鎖實現(xiàn);
- synchronized的鎖狀態(tài)是無法在代碼中直接判斷的,但是ReentrantLock可以通過ReentrantLock#isLocked判斷;
- synchronized是非公平鎖,ReentrantLock是可以是公平也可以是非公平的;
- synchronized是不可以被中斷的,而ReentrantLock#lockInterruptibly方法是可以被中斷的;
- 在發(fā)生異常時synchronized會自動釋放鎖,而ReentrantLock需要開發(fā)者在finally塊中顯示釋放鎖;
- ReentrantLock獲取鎖的形式有多種:如立即返回是否成功的tryLock(),以及等待指定時長的獲取,更加靈活;
- synchronized在特定的情況下對于已經(jīng)在等待的線程是后來的線程先獲得鎖(回顧一下sychronized的喚醒策略),而ReentrantLock對于已經(jīng)在等待的線程是先來的線程先獲得鎖;
1-2、ReentrantLock的使用
1-2-1、ReentrantLock同步執(zhí)行,類似synchronized
使用ReentrantLock需要注意的是:一定要在finally中進行解鎖,方式業(yè)務(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ù)代碼異常,無法釋放鎖
lock.unlock();
}
});
thread.start();
}
Thread.sleep(2000);
System.out.println(sum);
}
}
測試結(jié)果:

1-2-2、可重入鎖
可重入鎖就是 A(加鎖)-->調(diào)用--->B(加鎖)-->調(diào)用-->C(加鎖),從A到C即使B/C都有加鎖,也可以進入
@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來進行鎖中斷
lockInterruptibly()方法能夠中斷等待獲取鎖的線程。當兩個線程同時通過lock.lockInterruptibly()獲取某個鎖時,假若此時線程A獲取到了鎖,而線程B只有等待,那么對線程B調(diào)用threadB.interrupt()方法能夠中斷線程B的等待過程。
public class ReentrantLockDemo3 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
log.debug("t1啟動...");
try {
lock.lockInterruptibly();
try {
log.debug("t1獲得了鎖");
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
log.debug("t1等鎖的過程中被中斷");
}
}, "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、獲得鎖超時失敗
可以讓線程等待指定的時間,如果還未獲取鎖則進行失敗處理。
如下代碼,首先讓主線程獲得鎖,然后讓子線程啟動嘗試獲取鎖,但是由于主線程獲取鎖之后,讓線程等待了2秒,而子線程獲得鎖的超時時間只有1秒,如果未獲得鎖,則進行return失敗處理
public class ReentrantLockDemo4 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
log.debug("t1啟動...");
//超時
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 默認是不公平的

首先啟動500次for循環(huán)創(chuàng)建500個線程,然后進行加鎖操作,并同時啟動了。這樣這500個線程就依次排隊等待加鎖的處理
下面500個線程也是等待加鎖操作
如果使用公平鎖,下面500的線程只有等上面500個線程運行完成之后才能獲得鎖。
@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 之后去爭搶鎖
Thread.sleep(1000);
for (int i = 0; i < 500; i++) {
new Thread(() -> {
lock.lock();
try {
log.debug(Thread.currentThread().getName() + " running...");
} finally {
lock.unlock();
}
}, "強行插入" + i).start();
}
}
}
測試結(jié)果(后進入的線程都在等待排隊)

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

那ReentrantLock為什么默認使用非公平鎖呢?實際上就是為了提高性能,如果使用公平鎖,當前鎖對象釋放之后,還需要去隊列中獲取第一個排隊的線程,然后進行加鎖處理。而非公平鎖,可能再當前對象釋放鎖之后,正好有新的線程在獲取鎖,這樣就可以直接進行加鎖操作,不必再去隊列中讀取。
以上就是java ReentrantLock并發(fā)鎖使用詳解的詳細內(nèi)容,更多關(guān)于java ReentrantLock并發(fā)鎖的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springmvc接口接收參數(shù)與請求參數(shù)格式的整理
這篇文章主要介紹了springmvc接口接收參數(shù)與請求參數(shù)格式的整理,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
Spring Boot Actuator執(zhí)行器運行原理詳解
這篇文章主要介紹了Spring Boot Actuator執(zhí)行器運行原理詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-03-03
springboot之Validation參數(shù)校驗詳細解讀
這篇文章主要介紹了springboot之Validation參數(shù)校驗詳細解讀,本篇是關(guān)于springboot的參數(shù)校驗知識,當然也適用其它java應(yīng)用,讀完本篇將學會基本的參數(shù)校驗,自定義參數(shù)校驗和分組參數(shù)校驗,需要的朋友可以參考下2023-10-10
Spring Boot 利用WebUploader進行文件上傳功能
本文的重點是給大家介紹在Spring Boot項目中利用WebUploader如何進行文件上傳,本文通過示例代碼給大家介紹,需要的朋友參考下吧2018-03-03
springboot關(guān)于容器啟動事件總結(jié)
在本篇文章里小編給大家整理的是一篇關(guān)于springboot容器啟動事件相關(guān)知識點,需要的朋友們學習下。2019-10-10
SpringSecurity框架下實現(xiàn)CSRF跨站攻擊防御的方法
CSRF是一種網(wǎng)絡(luò)攻擊方式,也可以說是一種安全漏洞,這種安全漏洞在web開發(fā)中廣泛存在。這篇文章主要介紹了SpringSecurity框架下實現(xiàn)CSRF跨站攻擊防御,需要的朋友可以參考下2019-12-12
使用SpringMVC在redirect重定向的時候攜帶參數(shù)的問題
這篇文章主要介紹了使用SpringMVC在redirect重定向的時候攜帶參數(shù)的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03

