自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

干貨,深入剖析ReentrantLock源碼,推薦收藏

開發(fā) 前端
看完了ReentrantLock的所有源碼,是不是覺得ReentrantLock很簡單。由于加鎖流程的編排工作已經(jīng)在父類AQS中實(shí)現(xiàn),子類只需要實(shí)現(xiàn)具體的加鎖邏輯即可。

ReentrantLock和Synchronized都是Java開發(fā)中最常用的鎖,與Synchronized這種JVM內(nèi)置鎖不同的是,ReentrantLock提供了更豐富的語義。可以創(chuàng)建公平鎖或非公平鎖、響應(yīng)中斷、超時(shí)等待、按條件喚醒等。在某些場景下,使用ReentrantLock更適合,功能更強(qiáng)大。

前兩篇文章,我們分析了AQS的加鎖流程、以及源碼實(shí)現(xiàn)。當(dāng)時(shí)我們就說了,AQS使用了模板設(shè)計(jì)模式,父類中定義加鎖流程,子類去實(shí)現(xiàn)具體的加鎖邏輯。所以大部分加鎖代碼已經(jīng)在父類AQS中實(shí)現(xiàn)了,導(dǎo)致ReentrantLock的源碼非常簡單,一塊學(xué)習(xí)一下。

先看一下ReentrantLock怎么使用?

1. ReentrantLock的使用

/**
* @author 一燈架構(gòu)
* @apiNote ReentrantLock示例
**/
public class ReentrantLockDemo {

public static void main(String[] args){
// 1. 創(chuàng)建ReentrantLock對(duì)象
ReentrantLock lock = new ReentrantLock();
// 2. 加鎖
lock.lock();
try {
// 3. 這里執(zhí)行具體的業(yè)務(wù)邏輯
} finally {
// 4. 釋放鎖
lock.unlock();
}
}
}

可以看到ReentrantLock的使用非常簡單,調(diào)用lock加鎖,unlock釋放鎖,需要配置try/finally使用,保證在代碼執(zhí)行出錯(cuò)的時(shí)候也能釋放鎖。

ReentrantLock也可以配合Condition條件使用,具體可以翻一下前幾篇文章中BlockingQueue的源碼解析,那里面有ReentrantLock的實(shí)際使用。

再看一下ReentrantLock的類結(jié)構(gòu)

2. ReentrantLock類結(jié)構(gòu)

// 實(shí)現(xiàn)Lock接口
public class ReentrantLock implements Lock {

// 只有一個(gè)Sync同步變量
private final Sync sync;

// Sync繼承自AQS,主要邏輯都在這里面
abstract static class Sync extends AbstractQueuedSynchronizer {
}

// Sync的兩個(gè)子類,分別實(shí)現(xiàn)了公平鎖和非公平鎖
static final class FairSync extends Sync {
}
static final class NonfairSync extends Sync {
}

}

可以看出ReentrantLock的類結(jié)構(gòu)非常簡單,實(shí)現(xiàn)了Lock接口。

類里面有兩個(gè)靜態(tài)內(nèi)部類,分別實(shí)現(xiàn)公平鎖和非公平鎖。

看一下Lock接口中,定義了哪些方法?

public interface Lock {

// 加鎖
void lock();

// 加可中斷的鎖
void lockInterruptibly() throws InterruptedException;

// 嘗試加鎖
boolean tryLock();

// 一段時(shí)間內(nèi),嘗試加鎖
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

// 釋放鎖
void unlock();

// 新建條件狀態(tài)
Condition newCondition();
}

就是一些使用鎖的常用方法。

在上篇文章中瀏覽AQS源碼的時(shí)候,了解到AQS定義了一些有關(guān)具體加鎖、釋放鎖的抽象方法,留給子類去實(shí)現(xiàn),再看一下有哪些抽象方法:

// 加獨(dú)占鎖
protected boolean tryAcquire(int arg){
throw new UnsupportedOperationException();
}
// 釋放獨(dú)占鎖
protected boolean tryRelease(int arg){
throw new UnsupportedOperationException();
}

// 加共享鎖
protected int tryAcquireShared(int arg){
throw new UnsupportedOperationException();
}
// 釋放共享鎖
protected boolean tryReleaseShared(int arg){
throw new UnsupportedOperationException();
}

// 判斷是否是當(dāng)前線程正在持有鎖
protected boolean isHeldExclusively(){
throw new UnsupportedOperationException();
}

由于ReentrantLock使用的是獨(dú)占鎖,所以只需要實(shí)現(xiàn)獨(dú)占鎖相關(guān)的方法就可以了。

3.ReentrantLock源碼解析

3.1 ReentrantLock構(gòu)造方法

// 默認(rèn)的構(gòu)造方法,使用非公平鎖
public ReentrantLock(){
sync = new NonfairSync();
}

// 傳true,可以指定使用公平鎖
public ReentrantLock(boolean fair){
sync = fair ? new FairSync() : new NonfairSync();
}

在創(chuàng)建ReentrantLock對(duì)象的時(shí)候,可以指定使用公平鎖還是非公平鎖,默認(rèn)使用非公平鎖,顯然非公平鎖的性能更好。

先思考一個(gè)面試??紗栴},公平鎖和非公平鎖是怎么實(shí)現(xiàn)的?

3.2 非公平鎖源碼

先看一下加鎖源碼:

從父類ReentrantLock的加鎖方法入口:

public class ReentrantLock implements Lock {
// 加鎖入口方法
public void lock(){
// 調(diào)用Sync中加鎖方法
sync.lock();
}
}

在子類NonfairSync的加鎖方法:

// 非公平鎖
static final class NonfairSync extends Sync {

// 加鎖
final void lock(){
// 1. 先嘗試加鎖(使用CAS設(shè)置state=1
if (compareAndSetState(0, 1))
// 2. 加鎖成功,就把當(dāng)前線程設(shè)置為持有鎖線程
setExclusiveOwnerThread(Thread.currentThread());
else
// 3. 沒加鎖成功,再調(diào)用父類AQS中實(shí)際的加鎖邏輯
acquire(1);
}
}

加鎖邏輯也很簡單,先嘗試使用CAS加鎖(也就是把state從0設(shè)置成1),加鎖成功,就把當(dāng)前線程設(shè)置為持有鎖線程。

設(shè)計(jì)者很聰明,在鎖競爭不激烈的情況下,很大概率可以加鎖成功,也就不用走else中復(fù)雜的加鎖邏輯了。

如果沒有加鎖成功,還是需要走else中調(diào)用父類AQS的acquire方法,而acquire又需要調(diào)用子類的tryAcquire方法。

調(diào)用鏈路就是下面這樣:

圖片

根據(jù)調(diào)用鏈路,實(shí)際的加鎖邏輯在Sync.nonfairTryAcquire方法里面。

abstract static class Sync extends AbstractQueuedSynchronizer {
// 非公平鎖的最終加鎖方法
final boolean nonfairTryAcquire(int acquires){
final Thread current = Thread.currentThread();
// 1. 獲取同步狀態(tài)
int c = getState();
// 2. state=0表示無鎖,先嘗試加鎖(使用CAS設(shè)置state=1
if (c == 0) {
if (compareAndSetState(0, acquires)) {
// 3. 加鎖成功,就把當(dāng)前線程設(shè)置為持有鎖線程
setExclusiveOwnerThread(current);
return true;
}
// 4. 如果當(dāng)前線程已經(jīng)持有鎖,執(zhí)行可重入的邏輯
} else if (current == getExclusiveOwnerThread()) {
// 5. 加鎖次數(shù)+acquires
int nextc = c + acquires;
// 6. 超過tnt類型最大值,溢出了
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}

再看一下釋放鎖的調(diào)用流程,公平鎖和非公平鎖流程是一樣的,最終都是執(zhí)行Sync.tryRelease方法:

圖片

abstract static class Sync extends AbstractQueuedSynchronizer {
// 釋放鎖
protected final boolean tryRelease(int releases){
// 1. 同步狀態(tài)減去釋放鎖次數(shù)
int c = getState() - releases;
// 2. 校驗(yàn)當(dāng)前線程不持有鎖,就報(bào)錯(cuò)
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 3. 判斷同步狀態(tài)是否等于0,無鎖后,就刪除持有鎖的線程
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
}

再看一下公平鎖的源碼

3.3 公平鎖源碼

先看一下公平鎖的加鎖流程:

圖片

最終的加鎖方法是FairSync.tryAcquire,看一下具體邏輯:

static final class FairSync extends Sync {

// 實(shí)現(xiàn)父類的加鎖邏輯
protected final boolean tryAcquire(int acquires){
final Thread current = Thread.currentThread();
// 1. 獲取同步狀態(tài)
int c = getState();
// 2. state=0表示無鎖,先嘗試加鎖(使用CAS設(shè)置state=1
if (c == 0) {
// 3. 判斷當(dāng)前線程是不是頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)(講究先來后到)
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
// 4. 如果當(dāng)前線程已經(jīng)持有鎖,執(zhí)行可重入的邏輯
} else if (current == getExclusiveOwnerThread()) {
// 5. 加鎖次數(shù)+acquires
int nextc = c + acquires;
// 6. 超過tnt類型最大值,溢出了
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}

// 判斷當(dāng)前線程是不是頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)(講究先來后到)
public final boolean hasQueuedPredecessors(){
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
}

公平鎖的釋放鎖邏輯跟非公平鎖一樣,上面已經(jīng)講過。

4. 總結(jié)

看完了ReentrantLock的所有源碼,是不是覺得ReentrantLock很簡單。

由于加鎖流程的編排工作已經(jīng)在父類AQS中實(shí)現(xiàn),子類只需要實(shí)現(xiàn)具體的加鎖邏輯即可。

加鎖邏輯也很簡單,也就是修改同步狀態(tài)state的值和持有鎖的線程exclusiveOwnerThread。

責(zé)任編輯:武曉燕 來源: 一燈架構(gòu)
相關(guān)推薦

2020-08-24 08:13:25

非公平鎖源碼

2024-04-29 09:06:46

線程初始化源碼

2021-03-06 22:41:06

內(nèi)核源碼CAS

2020-11-09 07:29:12

ReentrantLo源碼公平鎖

2024-01-29 15:54:41

Java線程池公平鎖

2009-07-06 10:44:45

JSP charset

2009-09-27 17:13:36

Hibernate V

2010-06-03 13:08:51

2011-06-03 13:48:18

JavaScript重構(gòu)

2022-11-11 10:48:55

AQS源碼架構(gòu)

2010-05-25 12:59:00

Subversion

2009-09-14 15:12:40

LINQ to XML

2021-05-11 14:50:21

ReentrantLo可重入鎖Java

2010-08-04 13:52:53

Flex事件機(jī)制

2010-05-27 12:58:07

SVN升級(jí)

2009-09-25 09:36:55

Hibernate核心

2010-05-27 10:23:01

SVN文檔

2010-06-30 16:00:01

FTP協(xié)議

2010-06-12 14:35:46

UML對(duì)象圖

2019-04-18 10:00:29

Windows實(shí)用軟件
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)