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)前線程,并且支持對鎖獲取的中斷。 // 當(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í)被中斷,并且支持對鎖獲取中斷。 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:等待鎖的最長時(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)前線程,并且支持對鎖獲取的中斷。 // 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í)被中斷,并且支持對鎖獲取中斷。 boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 釋放鎖,與lock()、tryLock()、tryLock(long , TimeUnit)、lockInterruptibly()相對應(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ú)占鎖。可重入特性與synchronized相似,但擁有擴(kuò)展的功能。
ReentrantLock表現(xiàn)為API層面的互斥鎖,通過lock()和unlock()方法完成,是顯式的,而synchronized表現(xiàn)為原生語法層面的互斥鎖,是隱式的。
在JDK 1.6之后,虛擬機(jī)對于synchronized關(guān)鍵字進(jìn)行整體優(yōu)化后,在性能上synchronized與ReentrantLock已沒有明顯差距,因此在使用選擇上,需要根據(jù)場景而定,大部分情況下我們依然建議是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的方式競爭鎖,體現(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)行鎖競爭 // 搶鎖成功,則把當(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對象都存在一組監(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í)例對象建立更加精細(xì)的線程控制,也就帶來了更多靈活性了,我們可以簡單理解為以下兩點(diǎn):
- 通過Condition能夠精細(xì)的控制多線程的休眠與喚醒。
- 對于一個(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ì)列就相對于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都對應(yīng)著一個(gè)等待隊(duì)列,也就是說如果一個(gè)鎖上創(chuàng)建了多個(gè)Condition對象,那么也就存在多個(gè)等待隊(duì)列。等待隊(duì)列是一個(gè)FIFO的隊(duì)列,在隊(duì)列中每一個(gè)節(jié)點(diǎn)都包含了一個(gè)線程的引用,而該線程就是Condition對象上等待的線程。當(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對象對于一個(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í)行自旋操作爭取獲得鎖,同時(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ì)列的第一個(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ī)制的整個(gè)流程實(shí)現(xiàn)原理,流程如下圖所示(注意無論是同步隊(duì)列還是等待隊(duì)列使用的Node數(shù)據(jù)結(jié)構(gòu)都是同一個(gè),不過是使用的內(nèi)部變量不同罷了)
流程圖:
Condition它更強(qiáng)大的地方在于:能夠更加精細(xì)的控制多線程的休眠與喚醒。對于同一個(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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis-Plus自動(dòng)填充字段的詳細(xì)教程
今天編寫一個(gè)詳細(xì)的教程來介紹如何在?Spring?Boot?項(xiàng)目中使用?MyBatis-Plus?實(shí)現(xiàn)自動(dòng)填充時(shí)間字段(如創(chuàng)建時(shí)間?createTime?和更新時(shí)間?updateTime),可以分為以下幾個(gè)部分,這個(gè)教程將涵蓋從項(xiàng)目配置到自動(dòng)填充的完整過程,需要的朋友可以參考下2024-08-08