Java中的Lock與ReentrantLock深入解析
Lock
Lock位于java.util.concurrent.locks包下,是一種線程同步機(jī)制,就像synchronized塊一樣。但是,Lock比synchronized塊更靈活、更復(fù)雜。
1. Lock繼承關(guān)系

2. 官方文檔解讀


3. Lock接口方法解析
public interface Lock {
// 獲取鎖。如果鎖不可用,則當(dāng)前線程將出于線程調(diào)度目的而禁用,并處于休眠狀態(tài),直到獲得鎖為止。
void lock();
// 如果當(dāng)前線程未被中斷,則獲取鎖。如果鎖可用,則獲取鎖并立即返回。
// 如果鎖不可用,出于線程調(diào)度目的,將禁用當(dāng)前線程,該線程將一直處于休眠狀態(tài)。
// 下面兩種情形會(huì)讓當(dāng)前線程停止休眠狀態(tài):
// 1.鎖由當(dāng)前線程獲取。
// 2.其他一些線程中斷當(dāng)前線程,并且支持對(duì)鎖獲取的中斷。
// 當(dāng)前線程出現(xiàn)下面兩種情況時(shí),將拋出InterruptedException,并清除當(dāng)前線程的中斷狀態(tài)。
// 1.當(dāng)前線程在進(jìn)入此方法時(shí),已經(jīng)設(shè)置為中斷狀態(tài)。
// 2. 當(dāng)前線程在獲取鎖時(shí)被中斷,并且支持對(duì)鎖獲取中斷。
void lockInterruptibly() throws InterruptedException;
// 嘗試獲取鎖,如果鎖處于空閑狀態(tài),則獲取鎖,并立即返回true。如果鎖不可用,則立即返回false。
// 典型用法:
// 確保解鎖前一定獲取到鎖
// if (lock.tryLock()) {
// try {
// // manipulate protected state
// } finally {
// lock.unlock();
// }
// } else {
// // perform alternative actions
// }
boolean tryLock();
// 該方法為tryLock()的重載方法,兩個(gè)參數(shù)分別表示為:
// time:等待鎖的最長(zhǎng)時(shí)間
// unit:時(shí)間單位
// 如果在給定的等待時(shí)間內(nèi)是空閑的并且當(dāng)前線程沒有被中斷,則獲取鎖。如果鎖可用,則此方法立即獲取鎖并返回true,如果鎖不可用,出于線程調(diào)度目的,將禁用當(dāng)前線程,該線程將一直處于休眠狀態(tài)。
// 如果指定的等待時(shí)間超時(shí),則返回false值。如果時(shí)間小于或等于0,則該方法永遠(yuǎn)不會(huì)等待。
// 下面三種情形會(huì)讓當(dāng)前線程停止休眠狀態(tài):
// 1.鎖由當(dāng)前線程獲取。
// 2.其他一些線程中斷當(dāng)前線程,并且支持對(duì)鎖獲取的中斷。
// 3.到了指定的等待時(shí)間。
// 當(dāng)前線程出現(xiàn)下面兩種情況時(shí),將拋出InterruptedException,并清除當(dāng)前線程的中斷狀態(tài)。
// 1.當(dāng)前線程在進(jìn)入此方法時(shí),已經(jīng)設(shè)置為中斷狀態(tài)。
// 2.當(dāng)前線程在獲取鎖時(shí)被中斷,并且支持對(duì)鎖獲取中斷。
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 釋放鎖,與lock()、tryLock()、tryLock(long , TimeUnit)、lockInterruptibly()相對(duì)應(yīng)。
void unlock();
// 返回綁定到此鎖實(shí)例的Condition實(shí)例。當(dāng)前線程只有獲得了鎖,才能調(diào)用Condition實(shí)例的方法。
Condition newCondition();
}ReentrantLock
ReentrantLock位于java.util.concurrent(J.U.C)包下,是Lock接口的實(shí)現(xiàn)類,屬于獨(dú)占鎖??芍厝胩匦耘csynchronized相似,但擁有擴(kuò)展的功能。
ReentrantLock表現(xiàn)為API層面的互斥鎖,通過lock()和unlock()方法完成,是顯式的,而synchronized表現(xiàn)為原生語法層面的互斥鎖,是隱式的。
在JDK 1.6之后,虛擬機(jī)對(duì)于synchronized關(guān)鍵字進(jìn)行整體優(yōu)化后,在性能上synchronized與ReentrantLock已沒有明顯差距,因此在使用選擇上,需要根據(jù)場(chǎng)景而定,大部分情況下我們依然建議是synchronized關(guān)鍵字,原因之一是使用方便語義清晰,二是性能上虛擬機(jī)已為我們自動(dòng)優(yōu)化。而ReentrantLock提供了多樣化的同步特性,如超時(shí)獲取鎖、可以被中斷獲取鎖(synchronized的同步是不能中斷的)、等待喚醒機(jī)制的多個(gè)條件變量(Condition)等,因此當(dāng)我們確實(shí)需要使用到這些功能是,可以選擇ReentrantLock
ReentrantLock都是把具體實(shí)現(xiàn)委托給內(nèi)部類(Sync、NonfairSync、FairSync)
1. 可重入性
可重入性:任意線程在獲取到鎖之后能夠再次獲取該鎖而不會(huì)被鎖阻塞,synchronized和Reentrant都是可重入的,隱式顯式之分。
實(shí)現(xiàn)條件:
鎖需要去識(shí)別獲取鎖的線程是否是當(dāng)前占據(jù)鎖的線程,如果是的話,就成功獲取。鎖獲取一次,內(nèi)部鎖計(jì)數(shù)器需要加一,釋放一次減一,計(jì)數(shù)為零表示為成功釋放鎖。
ReentrantLock的重入計(jì)數(shù)是使用AbstractQueuedSynchronizer的state屬性的,state大于0表示鎖被占用,等于0表示空閑,小于0則是重入次數(shù)太多導(dǎo)致溢出了。
2. 公平鎖模式和非公平鎖模式
ReentrantLock的構(gòu)造函數(shù)接受可選的公平參數(shù),參數(shù)為true則表示獲取一個(gè)公平鎖,不帶參數(shù)或者false表示一個(gè)非公平鎖。
構(gòu)造方法:
// 無參構(gòu)造方法
// 默認(rèn)是非公平鎖模式
public ReentrantLock() {
sync = new NonfairSync();
}
// 有參構(gòu)造方法
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}公平性鎖和非公平性鎖父類:Sync
sync繼承于AQS
static abstract class Sync extends AbstractQueuedSynchronizer {
abstract void lock();
// 非公平獲取, 非公平鎖都需要這個(gè)方法
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// state == 0表示無鎖
// 通過CAS的方式競(jìng)爭(zhēng)鎖,體現(xiàn)非公平性,任何線程來了不用排隊(duì)都可以搶鎖
if (compareAndSetState(0, acquires)) {
// 加鎖成功,state狀態(tài)改為1
// 當(dāng)前哪一個(gè)線程獲取到鎖,將線程信息記錄到AQS里面
// 設(shè)置當(dāng)前持有鎖的線程
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) {
// 當(dāng)前線程正是鎖持有者,此段邏輯體現(xiàn)可重入性
// 同一個(gè)線程可以在不獲取鎖的情況再次進(jìn)入
// nextc表示被加鎖次數(shù),即重入次數(shù)
int nextc = c + acquires;
if (nextc < 0) // 被鎖次數(shù)上溢(很少出現(xiàn))
throw new Error("Maximum lock count exceeded");
// 設(shè)置加鎖次數(shù),lock幾次就要unlock幾次,否則無法釋放鎖
setState(nextc);
return true;
}
return false;
}
// 釋放
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
// 只有鎖的持有者才能釋放鎖
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) { // 鎖被釋放
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
// 當(dāng)前線程是否持有鎖
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
// 鎖的持有者
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
// 加鎖次數(shù)
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
// 是否上鎖,根據(jù)state字段可以判斷
final boolean isLocked() {
return getState() != 0;
}
}公平鎖模式:FairSync
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
// 方法邏輯與Snyc中的nonfairTryAcquire方法一致,只是加了線程排隊(duì)的邏輯
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// !hasQueuedPredecessors()
// 判斷隊(duì)列中是否有其他線程,沒有才進(jìn)行鎖的獲取,否則繼續(xù)排隊(duì)
// 體現(xiàn)公平性
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}非公平鎖模式:NonfairSync
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
// 不管鎖是否已經(jīng)被占用,采用CAS的方式進(jìn)行鎖競(jìng)爭(zhēng)
// 搶鎖成功,則把當(dāng)前線程設(shè)置為活躍的線程
// 搶鎖失敗則走acquire(1)邏輯
// 體現(xiàn)非公平性,線程不必排隊(duì)
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 搶鎖失敗,調(diào)用tryAcquire方法,最終調(diào)用nonfairTryAcquire方法
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}3. condition
在并發(fā)編程中,每個(gè)Java對(duì)象都存在一組監(jiān)視器方法,如wait()、notify()以及notifyAll()方法,通過這些方法,我們可以實(shí)現(xiàn)線程間通信與協(xié)作(也稱為等待喚醒機(jī)制),如生產(chǎn)者-消費(fèi)者模式、等待-通知模式,而且這些方法必須配合著synchronized關(guān)鍵字使用。
與synchronized的等待喚醒機(jī)制相比Condition具有更多的靈活性以及精確性,這是因?yàn)閚otify()在喚醒線程時(shí)是隨機(jī)(同一個(gè)鎖),而Condition則可通過多個(gè)Condition實(shí)例對(duì)象建立更加精細(xì)的線程控制,也就帶來了更多靈活性了,我們可以簡(jiǎn)單理解為以下兩點(diǎn):
- 通過Condition能夠精細(xì)的控制多線程的休眠與喚醒。
- 對(duì)于一個(gè)鎖,我們可以為多個(gè)線程間建立不同的Condition。
Condition相關(guān)方法
// 返回綁定到此鎖實(shí)例的Condition實(shí)例。當(dāng)前線程只有獲得了鎖,才能調(diào)用Condition實(shí)例的方法。
Condition newCondition();public interface Condition {
// 線程程進(jìn)入等待狀態(tài)直到被通知(signal)或中斷
// 當(dāng)其他線程調(diào)用singal()或singalAll()方法時(shí),該線程將被喚醒
// 當(dāng)其他線程調(diào)用interrupt()方法中斷當(dāng)前線程
// await()相當(dāng)于synchronized等待喚醒機(jī)制中的wait()方法
void await() throws InterruptedException;
// 與wait()方法相同,唯一的不同點(diǎn)是,該方法不會(huì)再等待的過程中響應(yīng)中斷
void awaitUninterruptibly();
// 當(dāng)前線程進(jìn)入等待狀態(tài),直到被喚醒或被中斷或超時(shí)
// 其中nanosTimeout指的等待超時(shí)時(shí)間,單位納秒
long awaitNanos(long nanosTimeout) throws InterruptedException;
// 同awaitNanos,但可以指明時(shí)間單位
boolean await(long time, TimeUnit unit) throws InterruptedException;
// 線程進(jìn)入等待狀態(tài),直到被喚醒、中斷或到達(dá)某個(gè)時(shí)
// 間期限(deadline),如果沒到指定時(shí)間就被喚醒,返回true,其他情況返回false
boolean awaitUntil(Date deadline) throws InterruptedException;
// 喚醒一個(gè)等待在Condition上的線程,該線程從等待方法返回前必須
// 獲取與Condition相關(guān)聯(lián)的鎖,功能與notify()相同
void signal();
// 喚醒所有等待在Condition上的線程,該線程從等待方法返回前必須
// 獲取與Condition相關(guān)聯(lián)的鎖,功能與notifyAll()相同
void signalAll();
}使用await之前必須加鎖,使用signal、signalAll之后記得釋放鎖。
Condition實(shí)現(xiàn)原理
Condition的具體實(shí)現(xiàn)類是AQS的內(nèi)部類ConditionObject,前面我們分析過AQS中存在兩種隊(duì)列,一種是同步隊(duì)列,一種是等待隊(duì)列,而等待隊(duì)列就相對(duì)于Condition而言的。注意在使用Condition前必須獲得鎖,同時(shí)在Condition的等待隊(duì)列上的結(jié)點(diǎn)與前面同步隊(duì)列的結(jié)點(diǎn)是同一個(gè)類即Node,其結(jié)點(diǎn)的waitStatus的值為CONDITION。在實(shí)現(xiàn)類ConditionObject中有兩個(gè)結(jié)點(diǎn)分別是firstWaiter和lastWaiter,firstWaiter代表等待隊(duì)列第一個(gè)等待結(jié)點(diǎn),lastWaiter代表等待隊(duì)列最后一個(gè)等待結(jié)點(diǎn),代碼如下:
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
/** First node of condition queue. */
// 等待隊(duì)列第一個(gè)等待結(jié)點(diǎn)
private transient Node firstWaiter;
/** Last node of condition queue. */
// 等待隊(duì)列最后一個(gè)等待結(jié)點(diǎn)
private transient Node lastWaiter;
/**
* Creates a new {@code ConditionObject} instance.
*/
public ConditionObject() { }
}每個(gè)Condition都對(duì)應(yīng)著一個(gè)等待隊(duì)列,也就是說如果一個(gè)鎖上創(chuàng)建了多個(gè)Condition對(duì)象,那么也就存在多個(gè)等待隊(duì)列。等待隊(duì)列是一個(gè)FIFO的隊(duì)列,在隊(duì)列中每一個(gè)節(jié)點(diǎn)都包含了一個(gè)線程的引用,而該線程就是Condition對(duì)象上等待的線程。當(dāng)一個(gè)線程調(diào)用了await()相關(guān)的方法,那么該線程將會(huì)釋放鎖,并構(gòu)建一個(gè)Node節(jié)點(diǎn)封裝當(dāng)前線程的相關(guān)信息加入到等待隊(duì)列中進(jìn)行等待,直到被喚醒、中斷、超時(shí)才從隊(duì)列中移出。Condition中的等待隊(duì)列模型如下:

Node節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu),在等待隊(duì)列中使用的變量與同步隊(duì)列是不同的,Condtion中等待隊(duì)列的結(jié)點(diǎn)只有直接指向的后繼結(jié)點(diǎn)并沒有指明前驅(qū)結(jié)點(diǎn),而且使用的變量是nextWaiter而不是next。
firstWaiter指向等待隊(duì)列的頭結(jié)點(diǎn),lastWaiter指向等待隊(duì)列的尾結(jié)點(diǎn),等待隊(duì)列中結(jié)點(diǎn)的狀態(tài)只有兩種即CANCELLED和CONDITION,前者表示線程已結(jié)束需要從等待隊(duì)列中移除,后者表示條件結(jié)點(diǎn)等待被喚醒。再次強(qiáng)調(diào)每個(gè)Codition對(duì)象對(duì)于一個(gè)等待隊(duì)列,也就是說AQS中只能存在一個(gè)同步隊(duì)列,但可擁有多個(gè)等待隊(duì)列。
實(shí)現(xiàn)代碼:
await方法:
public final void await() throws InterruptedException {
// 判斷線程是否被中斷
if (Thread.interrupted())
throw new InterruptedException();
// 創(chuàng)建新結(jié)點(diǎn)加入等待隊(duì)列并返回
Node node = addConditionWaiter();
// 釋放當(dāng)前線程鎖即釋放同步狀態(tài)
int savedState = fullyRelease(node);
int interruptMode = 0;
// 判斷結(jié)點(diǎn)是否同步隊(duì)列(SyncQueue)中,即是否被喚醒
while (!isOnSyncQueue(node)) {
// 掛起線程
LockSupport.park(this);
// 判斷是否被中斷喚醒,如果是退出循環(huán)。
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 被喚醒后執(zhí)行自旋操作爭(zhēng)取獲得鎖,同時(shí)判斷線程是否被中斷
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
// clean up if cancelled
if (node.nextWaiter != null)
// 清理等待隊(duì)列中不為CONDITION狀態(tài)的結(jié)點(diǎn)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
private Node addConditionWaiter() {
Node t = lastWaiter;
// 判斷是否為結(jié)束狀態(tài)的結(jié)點(diǎn)并移除
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
// 創(chuàng)建新結(jié)點(diǎn)狀態(tài)為CONDITION
Node node = new Node(Thread.currentThread(), Node.CONDITION);
// 加入等待隊(duì)列
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}signal()方法:
public final void signal() {
// 判斷是否持有獨(dú)占鎖,如果不是拋出異常
// 從這點(diǎn)也可以看出只有獨(dú)占模式先采用等待隊(duì)列,而共享模式下是沒有等待隊(duì)列的,也就沒法使用Condition
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
// 喚醒等待隊(duì)列第一個(gè)結(jié)點(diǎn)的線程
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {
// 移除條件等待隊(duì)列中的第一個(gè)結(jié)點(diǎn),
// 如果后繼結(jié)點(diǎn)為null,那么說沒有其他結(jié)點(diǎn)將尾結(jié)點(diǎn)也設(shè)置為null
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
// 如果被通知節(jié)點(diǎn)沒有進(jìn)入到同步隊(duì)列并且條件等待隊(duì)列還有不為空的節(jié)點(diǎn),則繼續(xù)循環(huán)通知后續(xù)結(jié)點(diǎn)
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
// transferForSignal方法
final boolean transferForSignal(Node node) {
// 嘗試設(shè)置喚醒結(jié)點(diǎn)的waitStatus為0,即初始化狀態(tài)
// 如果設(shè)置失敗,說明當(dāng)期結(jié)點(diǎn)node的waitStatus已不為
// CONDITION狀態(tài),那么只能是結(jié)束狀態(tài)了,因此返回false
// 返回doSignal()方法中繼續(xù)喚醒其他結(jié)點(diǎn)的線程,注意這里并
// 不涉及并發(fā)問題,所以CAS操作失敗只可能是預(yù)期值不為CONDITION,
// 而不是多線程設(shè)置導(dǎo)致預(yù)期值變化,畢竟操作該方法的線程是持有鎖的。
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 加入同步隊(duì)列并返回前驅(qū)結(jié)點(diǎn)p
Node p = enq(node);
int ws = p.waitStatus;
// 判斷前驅(qū)結(jié)點(diǎn)是否為結(jié)束結(jié)點(diǎn)(CANCELLED=1)或者在設(shè)置
// 前驅(qū)節(jié)點(diǎn)狀態(tài)為Node.SIGNAL狀態(tài)失敗時(shí),喚醒被通知節(jié)點(diǎn)代表的線程
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
// 喚醒node結(jié)點(diǎn)的線程
LockSupport.unpark(node.thread);
return true;
}流程:signal()被調(diào)用后,先判斷當(dāng)前線程是否持有獨(dú)占鎖,如果有,那么喚醒當(dāng)前Condition對(duì)象中等待隊(duì)列的第一個(gè)結(jié)點(diǎn)的線程,并從等待隊(duì)列中移除該結(jié)點(diǎn),移動(dòng)到同步隊(duì)列中,如果加入同步隊(duì)列失敗,那么繼續(xù)循環(huán)喚醒等待隊(duì)列中的其他結(jié)點(diǎn)的線程,如果成功加入同步隊(duì)列,那么如果其前驅(qū)結(jié)點(diǎn)是否已結(jié)束或者設(shè)置前驅(qū)節(jié)點(diǎn)狀態(tài)為Node.SIGNAL狀態(tài)失敗,則通過LockSupport.unpark()喚醒被通知節(jié)點(diǎn)代表的線程,到此signal()任務(wù)完成。
注意被喚醒后的線程,將從前面的await()方法中的while循環(huán)中退出,因?yàn)榇藭r(shí)該線程的結(jié)點(diǎn)已在同步隊(duì)列中,那么while (!isOnSyncQueue(node))將不在符合循環(huán)條件,進(jìn)而調(diào)用AQS的acquireQueued()方法加入獲取同步狀態(tài)的競(jìng)爭(zhēng)中,這就是等待喚醒機(jī)制的整個(gè)流程實(shí)現(xiàn)原理,流程如下圖所示(注意無論是同步隊(duì)列還是等待隊(duì)列使用的Node數(shù)據(jù)結(jié)構(gòu)都是同一個(gè),不過是使用的內(nèi)部變量不同罷了)
流程圖:

Condition它更強(qiáng)大的地方在于:能夠更加精細(xì)的控制多線程的休眠與喚醒。對(duì)于同一個(gè)鎖,我們可以創(chuàng)建多個(gè)Condition,在不同的情況下使用不同的Condition。例如,假如多線程讀/寫同一個(gè)緩沖區(qū):當(dāng)向緩沖區(qū)中寫入數(shù)據(jù)之后,喚醒"讀線程";當(dāng)從緩沖區(qū)讀出數(shù)據(jù)之后,喚醒"寫線程";并且當(dāng)緩沖區(qū)滿的時(shí)候,"寫線程"需要等待;當(dāng)緩沖區(qū)為空時(shí),"讀線程"需要等待。如果采用Object類中的wait(),notify(),notifyAll()實(shí)現(xiàn)該緩沖區(qū),當(dāng)向緩沖區(qū)寫入數(shù)據(jù)之后需要喚醒"讀線程"時(shí),不可能通過notify()或notifyAll()明確的指定喚醒"讀線程",而只能通過notifyAll喚醒所有線程(但是notifyAll無法區(qū)分喚醒的線程是讀線程,還是寫線程)。 但是,通過Condition,就能明確的指定喚醒讀線程。
利用Condition實(shí)現(xiàn)等待-通知模式:
public class ABCThread implements Runnable {
private ReentrantLock lock;
private Condition sCondition;
private Condition aCondition;
public ABCThread(ReentrantLock lock, Condition sCondition, Condition aCondition) {
this.lock = lock;
this.sCondition = sCondition; // 喚醒下一個(gè)線程
this.aCondition = aCondition; // 阻塞當(dāng)前線程
}
@Override
public void run() {
int i = 0;
while (i < 10) {
// 加鎖
lock.lock();
try {
// 接收到線程通知?jiǎng)t繼續(xù)執(zhí)行,否則就阻塞
aCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(Thread.currentThread().getName()+" ");
// 喚醒其他線程線程
sCondition.signal();
i++;
// 釋放鎖
lock.unlock();
}
}
}
ReentrantLock reentrantLock = new ReentrantLock();
Condition ab = reentrantLock.newCondition();
Condition bc = reentrantLock.newCondition();
Condition ca = reentrantLock.newCondition();
new Thread(new ABCThread(reentrantLock,ab,ca),"A").start();
new Thread(new ABCThread(reentrantLock,bc,ab),"B").start();
new Thread(new ABCThread(reentrantLock,ca,bc),"C").start();
reentrantLock.lock();
ca.signal();
reentrantLock.unlock();到此這篇關(guān)于Java中的Lock與ReentrantLock深入解析的文章就介紹到這了,更多相關(guān)Java的Lock與ReentrantLock內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java多線程并發(fā)JUC包ReentrantLock顯示鎖的用法
- Java使用ReentrantLock進(jìn)行加解鎖的示例代碼
- Java中的ReentrantLock實(shí)現(xiàn)原理及代碼演示
- Java可重入鎖reentrantLock解析
- Java并發(fā)編程之ReentrantLock解析
- Java并發(fā)編程中的ReentrantLock類詳解
- Java并發(fā)編程中的ReentrantLock詳解
- Java中的ReentrantLock原理解析
- 淺談一下Java中的ReentrantLock
- Java ReentrantLock的使用與應(yīng)用實(shí)戰(zhàn)
相關(guān)文章
SpringBoot中對(duì)SpringMVC的自動(dòng)配置詳解
這篇文章主要介紹了SpringBoot中的SpringMVC自動(dòng)配置詳解,Spring MVC自動(dòng)配置是Spring Boot提供的一種特性,它可以自動(dòng)配置Spring MVC的相關(guān)組件,簡(jiǎn)化了開發(fā)人員的配置工作,需要的朋友可以參考下2023-10-10
Springboot使用@RefreshScope注解實(shí)現(xiàn)配置文件的動(dòng)態(tài)加載
本文主要介紹了Springboot使用@RefreshScope注解實(shí)現(xiàn)配置文件的動(dòng)態(tài)加載,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
SpringMVC集成Web與MVC執(zhí)行流程和數(shù)據(jù)響應(yīng)及交互相關(guān)介紹全面總結(jié)
Spring MVC 是 Spring 提供的一個(gè)基于 MVC 設(shè)計(jì)模式的輕量級(jí) Web 開發(fā)框架,本質(zhì)上相當(dāng)于 Servlet,Spring MVC 角色劃分清晰,分工明細(xì),這篇文章主要介紹了SpringMVC集成Web與MVC執(zhí)行流程和數(shù)據(jù)響應(yīng)及交互2022-10-10
SpringBoot Pom文件依賴及Starter啟動(dòng)器詳細(xì)介紹
這篇文章主要介紹了SpringBoot Pom文件的依賴與starter啟動(dòng)器的作用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-09-09
springboot如何接收復(fù)雜參數(shù)(同時(shí)接收J(rèn)SON與文件)
文章介紹了在Spring Boot中同時(shí)處理JSON和文件上傳時(shí)使用`@RequestPart`注解的方法,`@RequestPart`可以接收多種格式的參數(shù),包括JSON和文件,并且可以作為`multipart/form-data`格式中的key2025-02-02
oracle+mybatis-plus+springboot實(shí)現(xiàn)分頁查詢的實(shí)例
本文主要介紹了oracle+mybatis-plus+springboot實(shí)現(xiàn)分頁查詢,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
Java 中 Date 與 Calendar 之間的編輯與轉(zhuǎn)換實(shí)例詳解
這篇文章主要介紹了Java 中 Date 與 Calendar 之間的編輯與轉(zhuǎn)換 ,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-07-07

