Java顯式鎖詳情
Java顯式鎖
一、顯式鎖
什么是顯式鎖?
由自己手動(dòng)獲取鎖,然后手動(dòng)釋放的鎖。
有了 synchronized(內(nèi)置鎖) 為什么還要 Lock(顯示鎖)?
使用 synchronized 關(guān)鍵字實(shí)現(xiàn)了鎖功能的,使用 synchronized 關(guān)鍵字將會(huì)隱式地獲取鎖,但是它將鎖的獲取和釋放固化了,也就是先獲取再釋放。
與內(nèi)置加鎖機(jī)制不同的是,Lock 提供了一種無條件的、可輪詢的、定時(shí)的以及可中斷的鎖獲取操作,所有加鎖和解鎖的方法都是顯式的。
二、Lock的常用api
| 方法名稱 | 描述 |
|---|---|
| void lock() | 獲取鎖 |
| void lockInterruptibly() throws InterruptedException | 可中斷的獲取鎖,和lock()方法的不同之處在于該方法會(huì)響應(yīng)中斷,即在鎖的獲取中可以中斷當(dāng)前線程 |
| boolean tryLock() | 嘗試非阻塞的獲取鎖,調(diào)用該方法后立刻返回,如果能夠獲取則返回true,否則返回false |
| boolean tryLock(long time, TimeUnit unit) throws InterruptedException | 超時(shí)獲取鎖,當(dāng)前線程會(huì)在以下三種情況下會(huì)返回: 1. 當(dāng)前線程在超時(shí)時(shí)間內(nèi)獲得了鎖 2.當(dāng)前線程在超市時(shí)間內(nèi)被中斷 3. 超時(shí)時(shí)間結(jié)束,返回false |
| void unlock(); | 釋放鎖 |
三、Lock的標(biāo)準(zhǔn)用法
lock.lock();
try {
// 業(yè)務(wù)邏輯
} finally {
lock.unlock();
}
- 在 finally 塊中釋放鎖,目的是保證在獲取到鎖之后,最終能夠被釋放。
- 不要將獲取鎖的過程寫在 try 塊中,因?yàn)槿绻讷@取鎖(自定義鎖的實(shí)現(xiàn))時(shí)發(fā)生了異常,異常拋出的同時(shí),也會(huì)導(dǎo)致鎖無故釋放。
四、ReentrantLock(可重入鎖)
Lock接口常用的實(shí)現(xiàn)類是 ReentrantLock。
示例代碼:主線程100000次減,子線程10萬次加。
public class ReentrantLockTest {
private Lock lock = new ReentrantLock();
private int count = 0;
public int getCount() {
return count;
}
private static class ReentrantLockThread extends Thread {
private ReentrantLockTest reentrantLockTest;
public ReentrantLockThread(ReentrantLockTest reentrantLockTest) {
this.reentrantLockTest = reentrantLockTest;
}
@Override
public void run() {
for (int i = 0; i < 100000; i++) {
reentrantLockTest.incr();
}
System.out.println(Thread.currentThread().getName() + " end, count = " + reentrantLockTest.getCount());
}
}
private void incr() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
private void decr() {
lock.lock();
try {
count--;
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ReentrantLockTest reentrantLockTest = new ReentrantLockTest();
new ReentrantLockThread(reentrantLockTest).start();
for (int i = 0; i < 100000; i++) {
// 遞減100000
reentrantLockTest.decr();
}
System.out.println(Thread.currentThread().getName() + " count = " + reentrantLockTest.getCount());
}
}
1. 鎖的可重入性
簡(jiǎn)單地講就是:“同一個(gè)線程對(duì)于已經(jīng)獲得到的鎖,可以多次繼續(xù)申請(qǐng)到該鎖的使用權(quán)”。而 synchronized 關(guān)鍵字隱式的支持重進(jìn)入,比如一個(gè) synchronized 修飾的遞歸方法,在方法執(zhí)行時(shí),執(zhí)行線程在獲取了鎖之后仍能連續(xù)多次地獲得該鎖
同樣,ReentrantLock 在調(diào)用 lock()方法時(shí),已經(jīng)獲取到鎖的線程,能夠再次調(diào)用lock()方法獲取鎖而不被阻塞
2. 公平鎖和非公平鎖
- 如果在時(shí)間上,先對(duì)鎖進(jìn)行獲取的請(qǐng)求一定先被滿足,那么這個(gè)鎖是公平的,反之,是不公平的。公平的獲取鎖,也就是等待時(shí)間最長(zhǎng)的線程最優(yōu)先獲取鎖,也可以說鎖獲取是順序的。
- ReentrantLock 提供了一個(gè)構(gòu)造函數(shù),能夠控制鎖是否是公平的(缺省為不公平鎖)。事實(shí)上,公平的鎖機(jī)制往往沒有非公平的效率高。
- 在激烈競(jìng)爭(zhēng)的情況下,非公平鎖的性能高于公平鎖的性能的一個(gè)原因是:在恢復(fù)一個(gè)被掛起的線程與該線程真正開始運(yùn)行之間存在著嚴(yán)重的延遲。
- 假設(shè)線程 A 持有一個(gè)鎖,并且線程 B 請(qǐng)求這個(gè)鎖,由于這個(gè)鎖已被線程 A 持有,因此 B 將被掛起。當(dāng) A 釋放鎖時(shí),B 將被喚醒,因此會(huì)再次嘗試獲取鎖。與此同時(shí),如果 C 也請(qǐng)求這個(gè)鎖,那么 C 很可能會(huì)在 B 被完全喚醒之前獲得、使用以及釋放這個(gè)鎖,這樣的情況是一種“雙贏”的局面:B 獲得鎖的時(shí)刻并沒有推遲,C 更早地獲得了鎖,完成了自己的任務(wù),然后釋放了鎖,并且吞吐量也獲得了提高。
五、ReentrantReadWriteLock(讀寫鎖)
ReentrantReadWriteLock 是 ReadWriteLock 的實(shí)現(xiàn)類。
之前提到鎖基本都是排他鎖,這些鎖在同一時(shí)刻只允許一個(gè)線程進(jìn)行訪問,而讀寫鎖在同一時(shí)刻可以允許多個(gè)讀線程訪問但是在寫線程訪問時(shí),所有的讀線程和其他寫線程均被阻塞。讀寫鎖維護(hù)了一對(duì)鎖,一個(gè)讀鎖和一個(gè)寫鎖,通過分離讀鎖和寫鎖,使得并發(fā)性相比一般的排他鎖有了很大提升。
讀鎖不排斥讀鎖,但是排斥寫鎖;寫鎖即排斥讀鎖也排斥寫鎖。
private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock getLock = lock.readLock(); // 讀鎖 private final Lock setLock = lock.writeLock(); // 寫鎖
至于上鎖、解鎖與 ReentrantLock 使用方式一致。
六、Condition
- 任意一個(gè) Java 對(duì)象,都擁有一組監(jiān)視器方法(定義在 java.lang.Object 上),主要包括 wait()、wait(long timeout)、notify()以及 notifyAll()方法,這些方法與 synchronized 同步關(guān)鍵字配合,可以實(shí)現(xiàn)等待/通知模式。
- Condition 接口也提供了類似 Object 的監(jiān)視器方法,與 Lock 配合可以實(shí)現(xiàn)等待/通知模式。
常用api
| 方法名稱 | 描述 |
|---|---|
| void await() throws InterruptedException | 使當(dāng)前線程進(jìn)入等待狀態(tài)直到被通知(signal)或中斷 |
| void signal() | 喚醒一個(gè)等待的線程 |
| void signalAll() | 喚醒所有等待的線程 |
示例代碼,主線程調(diào)用方法喚醒兩個(gè)子線程。
public class ConditionTest {
private volatile boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private void task1() {
lock.lock();
try {
try {
System.out.println(Thread.currentThread().getName() + " 等待中");
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 等待結(jié)束");
System.out.println("發(fā)送郵件");
} finally {
lock.unlock();
}
}
private void task2() {
lock.lock();
try {
try {
System.out.println(Thread.currentThread().getName() + " 等待中");
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 等待結(jié)束");
System.out.println("發(fā)送短信");
} finally {
lock.unlock();
}
}
private void updateFlag() {
lock.lock();
try {
this.flag = true;
this.condition.signalAll();
}finally {
lock.unlock();
}
}
private static class ConditionThread1 extends Thread {
private ConditionTest conditionTest;
public ConditionThread1(ConditionTest conditionTest) {
this.conditionTest = conditionTest;
}
@Override
public void run() {
if (!conditionTest.flag) {
conditionTest.task1();
}
}
}
private static class ConditionThread2 extends Thread {
private ConditionTest conditionTest;
public ConditionThread2(ConditionTest conditionTest) {
this.conditionTest = conditionTest;
}
@Override
public void run() {
if (!conditionTest.flag) {
conditionTest.task2();
}
}
}
public static void main(String[] args) throws InterruptedException {
ConditionTest conditionTest = new ConditionTest();
new ConditionThread1(conditionTest).start();
new ConditionThread2(conditionTest).start();
Thread.sleep(1000);
System.out.println("flag 改變。。。");
conditionTest.updateFlag();
}
}
以上就是Java顯式鎖詳情的詳細(xì)內(nèi)容,更多關(guān)于Java顯式鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java類中生成jfreechart,返回圖表的url地址 代碼分享
這篇文章介紹了java類中生成jfreechart,返回圖表的url地址的代碼,有需要的朋友可以參考一下2013-08-08
SpringBoot集成 Prometheus進(jìn)行高效監(jiān)控的實(shí)現(xiàn)
Prometheus作為一個(gè)開源的監(jiān)控和告警工具,以其強(qiáng)大的數(shù)據(jù)采集、存儲(chǔ)和查詢能力,受到了眾多開發(fā)者的青睞,本文主要介紹了SpringBoot集成 Prometheus進(jìn)行高效監(jiān)控的實(shí)現(xiàn),感興趣的可以了解一下2024-07-07
關(guān)于SpringCloud的微服務(wù)以及組件詳解
這篇文章主要介紹了關(guān)于SpringCloud的微服務(wù)以及組件詳解,是一個(gè)更高層次的、 架構(gòu)視角的綜合性大型項(xiàng)目, 他的目標(biāo)是構(gòu)建一套標(biāo)準(zhǔn)化的微服務(wù)解決方案,需要的朋友可以參考下2023-05-05
Java正確實(shí)現(xiàn)一個(gè)單例設(shè)計(jì)模式的示例
今天小編就為大家分享一篇關(guān)于Java正確實(shí)現(xiàn)一個(gè)單例設(shè)計(jì)模式的示例,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-01-01
關(guān)于對(duì)Java正則表達(dá)式"\\"的理解
正則表達(dá)式中,\代表轉(zhuǎn)義字符,通常是轉(zhuǎn)義一些特殊字符,下面這篇文章主要給大家介紹了關(guān)于對(duì)Java正則表達(dá)式"\\"的相關(guān)理解,需要的朋友可以參考下2022-09-09
JAVA中的函數(shù)式接口Function和BiFunction詳解
這篇文章主要介紹了JAVA中的函數(shù)式接口Function和BiFunction詳解,JDK的函數(shù)式接口都加上了@FunctionalInterface注解進(jìn)行標(biāo)識(shí),但是無論是否加上該注解只要接口中只有一個(gè)抽象方法,都是函數(shù)式接口,需要的朋友可以參考下2024-01-01

