ReentrantLock 是可重入的互斥鎖,雖然具有與 Synchronized 相同的功能,但比 Synchronized 更加靈活。 ReentrantLock 底層基于 AQS(AbstractQueuedSynchronizer)實(shí)現(xiàn)。

Reentrant 實(shí)現(xiàn)了 Lock 接口,其是 Java 對(duì)鎖操作行為的統(tǒng)一規(guī)范,Lock接口定義如下:
public interface Lock{//獲取鎖
void lock();
//獲取鎖-可以響應(yīng)中斷
void lockInterruptibly() throws InterruptedException;
//嘗試獲取一次鎖
boolean tryLock();
//返回獲取鎖是否成功狀態(tài) - 響應(yīng)中斷
boolean tryLock(long time,TimeUnit unit) throws InterrptedException;
//釋放鎖
void unlock();
//創(chuàng)建條件變量
Condition newCondition();
}1. ReentrantLock的使用使用 ReentrantLock 的 lock() 方法進(jìn)行鎖的獲取,即上鎖。使用 unlock() 方法進(jìn)行解鎖。
public class ReentrantLockDemo1 {public static void main(String[] args) {ReentrantLock lock = new ReentrantLock();
lock.lock();
try{ //臨界區(qū)代碼
}finally {//為避免臨界區(qū)代碼出現(xiàn)異常,導(dǎo)致鎖無(wú)法釋放,必須在finally中加上釋放鎖的語(yǔ)句
lock.unlock();
}
}
}ReentrantLock 也是可重入鎖:
public class ReentrantLockDemo1 {ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread() {@Override
public void run() {lock.lock();
try {//獲取鎖之后,在m1中進(jìn)行鎖的重入
m1();
} finally {lock.unlock();
}
}
private void m1() {lock.lock();
try {//臨界區(qū)
} finally {lock.unlock();
}
}
};
public static void main(String[] args) {ReentrantLockDemo1 demo = new ReentrantLockDemo1();
demo.t1.start();
}
}
默認(rèn)情況下,通過(guò)構(gòu)造方法new ReentrantLock()獲取的鎖為非公平鎖。
public class ReentrantLock{...
public ReentrantLock() {sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();
}
...
}為觀察公平與非公平的區(qū)別,我們嘗試如下程序:
public class ReentrantLockDemo1 {//啟用公平鎖
ReentrantLock lock = new ReentrantLock(true);
Runnable run = new Runnable() {@Override
public void run() {for (int i = 100; i >0; i--) {lock.lock();
try {System.out.println(Thread.currentThread().getName() + " got the lock");
} finally {lock.unlock();
}
}
}
};
public static void main(String[] args) {ReentrantLockDemo1 demo = new ReentrantLockDemo1();
Thread t1 = new Thread(demo.run,"t1");
Thread t2 = new Thread(demo.run,"t2");
t1.start();
t2.start();
}
}使用公平鎖時(shí),上述程序運(yùn)行結(jié)果:
t1 got the lock
t2 got the lock
t1 got the lock
t2 got the lock
t1 got the lock
t2 got the lock
t1 got the lock
...使用非公平鎖時(shí),上述程序運(yùn)行結(jié)果:
...
t1 got the lock
t1 got the lock
t1 got the lock
t1 got the lock
t2 got the lock
t2 got the lock
t2 got the lock
...ReentrantLock 首先調(diào)用 lock 方法嘗試獲取鎖資源。
public void lock() {sync.lock();
}開啟公平鎖時(shí),sync 對(duì)象為 FairSync 實(shí)例,開啟非公平鎖時(shí),sync 對(duì)象為 NonFairSync 對(duì)象。
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();
}觀察公平與非公平的 lock() 實(shí)現(xiàn)方式的不同。我們發(fā)現(xiàn):
公平鎖在獲取鎖的時(shí)候,會(huì)直接進(jìn)行 acquire() ,而非公平鎖則是直接嘗試 CAS 去更新鎖資源的 state 變量,更新成功則獲取到鎖資源,如果獲取不到,才會(huì)進(jìn)入 acquire()。這里涉及到 AQS 的 state 與 雙向鏈表數(shù)據(jù)結(jié)構(gòu),可以在AQS專題學(xué)習(xí)。
CAS + volatile 實(shí)現(xiàn)線程安全地更新變量CAS(CompareAndSwap):在Java中,使用Unsafe類的compareAndSet()方法可以通過(guò)底層的 lock cmpxchg 指令實(shí)現(xiàn)原子性操作。
volatile :保證了線程間的變量一致性,即可見性。
CAS + Volatile:多線程場(chǎng)景中,某個(gè)個(gè)線程通過(guò) CAS 將 volatile 修飾的變量更新成功后,所有線程在使用該變量時(shí),都可見該變量的最新值。從而保證,在多線程場(chǎng)景下,對(duì)該變量的修改,不會(huì)引起線程安全問(wèn)題。
static final class FairSync extends Sync {...
final void lock() {//直接進(jìn)入acquire
acquire(1);
}
}
static final class NonfairSync extends Sync { ...
final void lock() {//先嘗試更新AQS的State競(jìng)爭(zhēng)鎖
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//直接獲取鎖失敗時(shí),才會(huì)進(jìn)入acquire
acquire(1);
}
}compareAndSetState():調(diào)用 Unsafe類提供的native層的原子性 CAS 操作。修改 AQS 中的 state 變量。
protected final boolean compareAndSetState(int expect, int update) {return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}setExclusiveOwnerThread():在 AQS 中將當(dāng)前線程設(shè)置為鎖的持有者
protected final void setExclusiveOwnerThread(Thread thread) {exclusiveOwnerThread = thread;
}step2: accquire() —— 模板方法FairSync 與 NonFairSync 都是 AQS 的子類,acquire() 是 AQS 向子類提供的模板方法。其中 tryAcquire() 方法需要子類重寫實(shí)現(xiàn)。
public final void acquire(int arg) {//先根據(jù)公平與非公平不同的方式,進(jìn)行嘗試獲取鎖
if (!tryAcquire(arg) &&
//如果獲取失敗,則排隊(duì)等待
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}step3: tryAquire() 的不同實(shí)現(xiàn)tryAquire()方法需要子類重寫實(shí)現(xiàn),在 AQS 中,該方法僅拋出一個(gè)異常:
protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();
}先看到公平鎖對(duì) tryAquire() 的實(shí)現(xiàn)。公平鎖的 tryAquire() 主要做了:
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread();
//查看是否有人持有鎖
int c = getState();
if (c == 0) {//如果沒(méi)有人持有鎖
//查看是否有人排隊(duì),如果沒(méi)人排隊(duì)則嘗試CAS獲取鎖
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {//獲取鎖成功,將AQS持有鎖的線程設(shè)置為本線程
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {//鎖的重入
int nextc = c + acquires;
if (nextc< 0)
throw new Error("Maximum lock count exceeded");
//這里可以直接設(shè)置,同一個(gè)線程,不會(huì)有線程安全。
//state>0表示有人持有鎖,state的具體數(shù)值表示鎖的重入次數(shù)
setState(nextc);
return true;
}
return false;
}而非公平鎖則不同,非公平鎖的 tryAquire() 主要做了:
final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {//如果鎖資源空閑,直接CAS嘗試獲取鎖
if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {//鎖的重入
int nextc = c + acquires;
//重入次數(shù)過(guò)多,int類型會(huì)overflow變成負(fù)數(shù)
if (nextc< 0) // overflow
throw new Error("Maximum lock count exceeded");
//鎖重入,直接設(shè)置新的值,不會(huì)有線程安全問(wèn)題
setState(nextc);
return true;
}
return false;
}step4. 入隊(duì)并阻塞線程,由 AQS 實(shí)現(xiàn)在acquire()模板方法中,如果tryAquire()沒(méi)有獲取到鎖,將會(huì)準(zhǔn)備在 AQS 中排隊(duì)。主要工作:
public final void acquire(int arg) {if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
private Node addWaiter(Node mode) {//將要入隊(duì)的線程封裝到 AQS 的 Node結(jié)構(gòu) 中
Node node = new Node(Thread.currentThread(), mode);
//獲取隊(duì)尾元素
Node pred = tail;
//如果隊(duì)尾元素不為空,則跟在隊(duì)尾元素之后
if (pred != null) {node.prev = pred;
//通過(guò)CAS保證線程安全地入隊(duì)
if (compareAndSetTail(pred, node)) {pred.next = node;
return node;
}
}
//如果入隊(duì)失敗,通過(guò)enq來(lái)循環(huán)CAS,自旋嘗試入隊(duì)
enq(node);
return node;
}
private Node enq(final Node node) {for (;;) {Node t = tail;
//如果隊(duì)尾為空,說(shuō)明隊(duì)中沒(méi)有元素,連head都沒(méi)有
if (t == null) {// Must initialize
//cas 使隊(duì)頭隊(duì)尾指針指向空Node
//head->Node()<- tail
if (compareAndSetHead(new Node()))
tail = head;
} else {//線程安全地嘗試插入隊(duì)尾
node.prev = t;
if (compareAndSetTail(t, node)) {t.next = node;
return t;
}
}
}
}通過(guò)addWaiter(),不論公平鎖還是非公平鎖,都將當(dāng)前線程包裝在Node結(jié)構(gòu)中,并插入到 AQS 的雙向鏈表的隊(duì)列末尾。
而后在 acquireQueued() 中,視情況再次獲取鎖,或者直接嘗試阻塞線程:
hasQueuedPredecessors()判斷為真)final boolean acquireQueued(final Node node, int arg) {boolean failed = true;
try {boolean interrupted = false;
for (;;) {//獲取前驅(qū)節(jié)點(diǎn)
final Node p = node.predecessor();
//如果前驅(qū)是head(head是空節(jié)點(diǎn)),說(shuō)明當(dāng)前Node排在隊(duì)頭,嘗試獲取所資源
if (p == head && tryAcquire(arg)) {//如果獲取資源成功,則不阻塞當(dāng)前線程,而是return回去,繼續(xù)程序的執(zhí)行
//同時(shí)將包裝當(dāng)前的Node清空,并變?yōu)樾碌膆ead
setHead(node);
//將原來(lái)的頭清空應(yīng)用,等待GC回收
p.next = null; // help GC
failed = false;
return interrupted;
}
//如果爭(zhēng)鎖失敗,將會(huì)準(zhǔn)備阻塞,如果本次準(zhǔn)備失敗,將會(huì)再循環(huán)一次到這里,準(zhǔn)備成功即可阻塞。
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {...
}
}shouldParkAfterFailedAcquire()做了阻塞線程前的判斷,主要工作是:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {int ws = pred.waitStatus;
if (ws == Node.SIGNAL)//waitStatus = -1
return true;//前驅(qū)結(jié)點(diǎn)正常,當(dāng)前線程可以阻塞
if (ws >0) {//waitStatus = CANCELLED = 1
do {//更新前驅(qū)節(jié)點(diǎn),將node的前驅(qū)引用指向更前一個(gè)
//pred = pred.prev;
//node.prev = pred;
node.prev = pred = pred.prev;
} while (pred.waitStatus >0);
//最后將可用的前驅(qū)結(jié)點(diǎn)指向node自己,從而拋棄中間若干個(gè)廢棄的節(jié)點(diǎn)
pred.next = node;
} else {//如果node的waitStatus<0 但不是-1,只需要都統(tǒng)一更新為-1即可。
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}準(zhǔn)備工作完成,就可以進(jìn)入線程阻塞,parkAndCheckInterrupt()方法通過(guò)Unsafe類實(shí)現(xiàn)線程的阻塞。
private final boolean parkAndCheckInterrupt() {LockSupport.park(this);
return Thread.interrupted();
}
//LockSupport:
public static void park(Object blocker) {Thread t = Thread.currentThread();
//設(shè)置寫屏障 write barrier
setBlocker(t, blocker);
//通過(guò)UNSAFE類的park()native方法進(jìn)行線程的阻塞。
UNSAFE.park(false, 0L);
//設(shè)置寫屏障 write barrier
setBlocker(t, null);
}
2.2 公平鎖與非公平鎖的解鎖實(shí)現(xiàn)不論是公平鎖還是非公平鎖,解鎖的實(shí)現(xiàn)是一致的:
//ReentrantLock
public void unlock() {sync.release(1);
}
//Sync extends AQS
public final boolean release(int arg) {//解鎖
if (tryRelease(arg)) {Node h = head;
//如果解鎖完成,如果隊(duì)列中有元素,則喚醒隊(duì)列中的某個(gè)線程
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
//Sync
protected final boolean tryRelease(int releases) {//計(jì)算本次解鎖后 state 的值
int c = getState() - releases;
//如果要解鎖的不是持有鎖的線程,說(shuō)明程序出了問(wèn)題
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//如果解鎖后的值為 0,說(shuō)明徹底解鎖
if (c == 0) {free = true;
//去掉 AQS 對(duì)持有鎖線程的引用
setExclusiveOwnerThread(null);
}
//設(shè)置新的state值
setState(c);
return free;
}
其中,解鎖后需要喚醒隊(duì)列中的某個(gè)線程,主要流程是:
private void unparkSuccessor(Node node) {//獲取頭結(jié)點(diǎn)的waitStatus
int ws = node.waitStatus;
if (ws< 0)
//如果頭結(jié)點(diǎn)是個(gè)被復(fù)用的空節(jié)點(diǎn),把它設(shè)置為初始化狀態(tài),即waitStatus = 0
compareAndSetWaitStatus(node, ws, 0);
//獲取頭結(jié)點(diǎn)的后繼節(jié)點(diǎn)
Node s = node.next;
//如果沒(méi)有后繼節(jié)點(diǎn),或者后繼節(jié)點(diǎn)廢棄
if (s == null || s.waitStatus >0) {s = null;
//從隊(duì)尾往前尋找一個(gè)可用的節(jié)點(diǎn)
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus<= 0)
s = t;
}
//如果最后找到一個(gè)可用的節(jié)點(diǎn),那么喚醒其綁定的線程
if (s != null)
LockSupport.unpark(s.thread);
}至此,ReentrantLock 的加鎖與解鎖全部分析完成。最后附上非公平鎖的加鎖時(shí)序圖:
3.為什么Sync實(shí)現(xiàn)nonfairTryAcquire()?因?yàn)?tryLock() 沒(méi)有公平與非公平的概念,都是走非公平的邏輯,調(diào)用sync.nonfaireTryAquire(),即:
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧
當(dāng)前標(biāo)題:Java多線程(二)——ReentrantLock源碼分析-創(chuàng)新互聯(lián)
轉(zhuǎn)載源于:http://www.chinadenli.net/article36/dcsesg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設(shè)、自適應(yīng)網(wǎng)站、關(guān)鍵詞優(yōu)化、App設(shè)計(jì)、用戶體驗(yàn)、ChatGPT
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容