日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
面試官:有了解過ReentrantLock的底層實(shí)現(xiàn)嗎?說說看

源碼剖析

??上節(jié)??帶大家學(xué)習(xí)了它的基本使用,我們可以了解到它是一個可重入鎖,下面我們就一起看一下它的底層實(shí)現(xiàn)~

創(chuàng)新互聯(lián)是專業(yè)的烏恰網(wǎng)站建設(shè)公司,烏恰接單;提供網(wǎng)站制作、網(wǎng)站建設(shè),網(wǎng)頁設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行烏恰網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來合作!

構(gòu)造函數(shù)

我們在使用的時(shí)候,都是先new它,所以我們先看下它的構(gòu)造函數(shù),它主要有兩個:

public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}

從字面上看,它們之間的不同點(diǎn)在于fair,翻譯過來就是公平的意思,大體可以猜到它是用來構(gòu)建公平鎖和非公平鎖,在繼續(xù)往下看源碼之前,先給大家科普一下這兩種鎖。

公平鎖 & 非公平鎖

  • 公平鎖多個線程按照申請鎖的順序去獲得鎖,線程會直接進(jìn)入隊(duì)列去排隊(duì),永遠(yuǎn)都是隊(duì)列的第一位才能得到鎖。(例如銀行辦業(yè)務(wù)取號)。

這種鎖的優(yōu)點(diǎn)很明顯,每個線程都能夠獲取資源,缺點(diǎn)也很明顯,如果某個線程阻塞了,其它線程也會阻塞,然而cpu喚醒開銷很大,之前也給大家講過。

  • 非公平鎖多個線程都去嘗試獲取鎖,獲取不到就進(jìn)入等待隊(duì)列,cpu也不用去喚醒。

優(yōu)缺點(diǎn)正好和上邊相反,優(yōu)點(diǎn)減少開銷,缺點(diǎn)也很明顯,可能會導(dǎo)致一直獲取不到鎖或長時(shí)間獲取不到鎖。

好,有了基本概念之后,我們繼續(xù)往下看。

NonfairSync

首先,我們看下非公平鎖,默認(rèn)情況下,我們申請的都是非公平鎖,也就是new ReentrantLock(),我們接著看源碼。

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() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}

它繼承了Sync,Sync是一個內(nèi)容靜態(tài)抽象類:

abstract static class Sync extends AbstractQueuedSynchronizer {...}

分為公平和非公平,使用AQS狀態(tài)來表示持鎖的次數(shù),在構(gòu)造函數(shù)初始化的時(shí)候都有sync = ...,我們接著看NonfairSync。在使用的時(shí)候,我們調(diào)用了lock.lock()方法,它是ReentrantLock的一個實(shí)例方法。

// 獲取鎖
public void lock() {
sync.lock();
}

實(shí)際上內(nèi)部還是調(diào)了sync的內(nèi)部方法,因?yàn)槲覀兩暾埖氖欠枪芥i,所以我們看NonfairSync下的lock實(shí)現(xiàn):

final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}

compareAndSetState這個方法,是AQS的內(nèi)部方法,意思是如果當(dāng)前狀態(tài)值等于預(yù)期值,則自動將同步狀態(tài)設(shè)置為給定的更新值。此操作具有volatile讀寫的內(nèi)存語義。

protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

可以看到執(zhí)行l(wèi)ock方法,會通過AQS機(jī)制計(jì)數(shù),setExclusiveOwnerThread設(shè)置線程獨(dú)占訪問權(quán)限,它是AbstractOwnableSynchronizer的一個內(nèi)部方法,子類通過使用它來管理線程獨(dú)占。

public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {}

可以看到它是繼承了AbstractOwnableSynchronizer。下面接著看,我們說如果實(shí)際值等于期望值會執(zhí)行上邊的方法,不期望的時(shí)候會執(zhí)行acquire(1)。

public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}

這個方法以獨(dú)占模式獲取,忽略中斷,它會嘗試調(diào)用tryAcquire,成功會返回,不成功進(jìn)入線程排隊(duì),可以重復(fù)阻塞和解除阻塞??聪翧QS 內(nèi)部的這個方法。

protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}

我們可以看到實(shí)現(xiàn)肯定不在這,它的具體實(shí)現(xiàn)在NonfairSync。

protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}

可以看到它調(diào)用了,nonfairTryAcquire方法,這個方法是不公平的tryLock,具體實(shí)現(xiàn)在Sync內(nèi)部,這里我們要重點(diǎn)關(guān)注一下。

final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 返回同步狀態(tài)值,它是AQS內(nèi)部的一個方法
// private volatile int state;
// protected final int getState() {
// return state;
// }
int c = getState();
if (c == 0) {
// 為0就比較一下,如果與期望值相同就設(shè)置為獨(dú)占線程,說明鎖已經(jīng)拿到了
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 否則 判斷如果當(dāng)前線程已經(jīng)是被設(shè)置獨(dú)占線程了
else if (current == getExclusiveOwnerThread()) {

// 設(shè)置當(dāng)前線程狀態(tài)值 + 1 并返回成功
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
// 否則返回失敗 沒拿到鎖
return false;
}

好,我們再回過頭看下 acquire。

public final void acquire(int arg) {
// 如果當(dāng)前線程沒有獲取到鎖 并且 在隊(duì)列中的線程嘗試不斷拿鎖如果被打斷了會返回true, 就會調(diào)用 selfInterrupt
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}

selfInterrupt很好理解,線程中斷。

static void selfInterrupt() {
Thread.currentThread().interrupt();
}

其實(shí)我們關(guān)注的重點(diǎn)是這個方法acquireQueued,首先關(guān)注一下入?yún)?它內(nèi)部傳入了一個addWaiter,最后它回NODE節(jié)點(diǎn)。

private Node addWaiter(Node mode) {
// mode 沒啥好說的就是一個標(biāo)記,用于標(biāo)記獨(dú)占模式 static final Node EXCLUSIVE = null;
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}

我們可以大體從猜到,Node是一個等待隊(duì)列的節(jié)點(diǎn)類,是一個鏈表結(jié)構(gòu),之前我們講FutureTask源碼的時(shí)候也遇到過這種結(jié)構(gòu),它通常用于自旋鎖,在這個地方,它是用于阻塞同步器。

+------+  prev +-----+       +-----+
head | | <---- | | <---- | | tail
+------+ +-----+ +-----+

好,下面我們關(guān)注一下 acquireQueued。

final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
// 默認(rèn)是 false
boolean interrupted = false;
// 進(jìn)入阻塞循環(huán)遍歷 線程隊(duì)列
for (;;) {
// 返回前一個節(jié)點(diǎn)
final Node p = node.predecessor();

// 判斷如果前一個節(jié)點(diǎn)是頭部節(jié)點(diǎn),并且拿到鎖了,就會設(shè)置當(dāng)前節(jié)點(diǎn)為頭部節(jié)點(diǎn)
if (p == head && tryAcquire(arg)) {
setHead(node);
// 這里可以看到注釋 help gc ,
p.next = null; // help GC
failed = false;
return interrupted;
}
// 檢查并更新未能獲取的節(jié)點(diǎn)的狀態(tài)。如果線程應(yīng)該阻塞,則返回 true 并且線程中斷了
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
// 如果失敗 取消正在嘗試獲取的節(jié)點(diǎn)
if (failed)
cancelAcquire(node);
}
}

從上面的源碼來看,在體會一下上面講的非公平鎖的概念,是不是更好理解一些,然后就是釋放鎖unlock,這個方法我們可以看到是ReentrantLock下的一個實(shí)例方法,所以公平鎖的釋放鎖也是調(diào)的這個方法,其實(shí)最終可以猜到調(diào)用的還是sync的方法。

public void unlock() {
sync.release(1);
}

Sync繼承AQS,release是AQS的內(nèi)部方法。

public final boolean release(int arg) {
// 嘗試釋放鎖 tryRelease 在Sync內(nèi)部
if (tryRelease(arg)) {
Node h = head;
// 如果節(jié)點(diǎn)存在 并且狀態(tài)值不為0
if (h != null && h.waitStatus != 0)
// 喚醒下個節(jié)點(diǎn)
unparkSuccessor(h);
return true;
}
return false;
}
private void unparkSuccessor(Node node) {    
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);

Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
// 可以看到調(diào)用了 LockSupport來喚醒
LockSupport.unpark(s.thread);
}

我們再看下tryRelease, 同樣這個實(shí)現(xiàn)在Sync內(nèi)。

protected final boolean tryRelease(int releases) {
// 同樣釋放鎖的時(shí)候 依然使用 AQS計(jì)數(shù)
int c = getState() - releases;
// 判斷當(dāng)前線程是否是獨(dú)占線程,不是拋出異常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 如果是0 表示是釋放成功
if (c == 0) {
free = true;
// 并且把獨(dú)占線程設(shè)為null
setExclusiveOwnerThread(null);
}
// 更新狀態(tài)值
setState(c);
return free;
}

FairSync

公平鎖FairSync的區(qū)別在于,它的獲取鎖的實(shí)現(xiàn)在它的內(nèi)部,Sync默認(rèn)內(nèi)部實(shí)現(xiàn)了非公平鎖。

static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;

// 這個方法最終調(diào)用 tryAcquire
final void lock() {
acquire(1);
}
// 公平鎖的實(shí)現(xiàn)
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 這邊和非公平鎖的實(shí)現(xiàn)有些相似 同樣判斷狀態(tài)
if (c == 0) {
// 判斷排隊(duì)隊(duì)列是否存在, 不存在并且比較期望值
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
// 設(shè)置獨(dú)占線程 并返回成功
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;
}
}

它的實(shí)現(xiàn)比較簡單,通過實(shí)現(xiàn)可以發(fā)現(xiàn),它按照申請鎖的順序來獲取鎖,排第一的先拿到鎖,在結(jié)合上面的概念理解一下,就很好理解了。

釋放鎖unlock,上面我們已經(jīng)講過了。

結(jié)束語

本節(jié)內(nèi)容可能有點(diǎn)多,主要是看源碼,可以打斷點(diǎn)自己調(diào)一下, 舉一反三,通過源碼去理解一下什么是公平鎖和非公平鎖, ReentrantLock可重入鎖體驗(yàn)在哪里。


標(biāo)題名稱:面試官:有了解過ReentrantLock的底層實(shí)現(xiàn)嗎?說說看
轉(zhuǎn)載來于:http://www.dlmjj.cn/article/dhgcodc.html