日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

Java8 ReentrantLock 源码分析

發(fā)布時(shí)間:2024/9/30 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java8 ReentrantLock 源码分析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、ReentrantLock 概述

1.1 ReentrantLock 簡介

故名思義,ReentrantLock 意為可重入鎖,那么什么是可重入鎖呢?可重入意為一個(gè)持有鎖的線程可以對(duì)資源重復(fù)加鎖而不會(huì)阻塞。比如下面這樣:

public synchronized void f1() {f2();}private synchronized void f2() { }

ReentrantLock 除了支持可重入以外,還支持定義公平與非公平策略,默認(rèn)情況下采用非公平策略。公平是指等待時(shí)間最長的線程會(huì)優(yōu)先獲取鎖,也就是獲取鎖是順序的,可以理解為先到先得。

公平鎖保證了鎖的獲取按照 FIFO(先進(jìn)先出)原則,但是需要大量的線程切換。非公平鎖雖然減少了線程之間的切換增大了其吞吐量,但是可能會(huì)造成線程“饑餓”。

1.2 使用方式

public void f1() {ReentrantLock lock = new ReentrantLock();try {lock.lock();// ...} finally {lock.unlock();}}

與 synchronized 不同,使用 ReentrantLock 必須顯示的加鎖與釋放鎖。

1.3 與 synchronized 對(duì)比

相同點(diǎn):

  • 可重入,同一線程可以多次獲得同一個(gè)鎖
  • 都保證了可見性和互斥性

不同點(diǎn):

  • ReentrantLock 可響應(yīng)中斷、可輪回,為處理鎖的不可用性提供了更高的靈活性,synchronized 不可以響應(yīng)中斷
  • ReentrantLock 是 API 級(jí)別的,synchronized 是 JVM 級(jí)別(JVM 內(nèi)置屬性)的,因此內(nèi)置鎖可以與特定的棧幀關(guān)聯(lián)起來
  • ReentrantLock 可以實(shí)現(xiàn)公平鎖,切可以實(shí)現(xiàn)帶有時(shí)間限制的操作
  • ReentrantLock 通過 Condition 可以綁定多個(gè)條件

二、源碼分析

了解了 ReentrantLock 的基本概念后,接下來就一起來看源碼吧。其實(shí) ReentrantLock 內(nèi)的源碼并不多,原因在于很多源碼都在 AbstractQueuedSynchronizer 中,因此想要了解 ReentrantLock 源碼的小伙伴需要先行了解下 AQS。

后面會(huì)給大家推薦幾篇關(guān)于 AQS 源碼介紹的文章,有興趣的可以自行了解,或者到我的 GitHub 去查下相應(yīng)的源碼記錄。點(diǎn)我前往 GitHub

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

默認(rèn)構(gòu)造函數(shù)

public ReentrantLock() {sync = new NonfairSync();}

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

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

上面我們說了 ReentrantLock 支持兩種同步策略,分別是公平與非公平,從這個(gè)構(gòu)造函數(shù)中就能體現(xiàn)出來,相信大家看了這個(gè)構(gòu)造函數(shù)也應(yīng)該想到這兩個(gè) FairSync,NonfairSync 對(duì)象應(yīng)該發(fā)揮著至關(guān)重要的作用。下面就來分別分析下對(duì)應(yīng)的源碼。

2.2 NonfairSync

static final class NonfairSync extends Sync {private static final long serialVersionUID = 7316153563782823691L;// 加鎖final void lock() {// 無鎖重入,通過 CAS 第一次嘗試修改同步狀態(tài)值if (compareAndSetState(0, 1))// 設(shè)置當(dāng)前線程獨(dú)占setExclusiveOwnerThread(Thread.currentThread());else// 鎖重入,同步狀態(tài)值 + 1acquire(1);}// 嘗試獲取鎖,直接調(diào)用 Sync 中的 nonfairTryAcquire 方法即可protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}}

acquire 方法是 AQS 中的一個(gè)模板方法,這里我們就不多介紹了,還是要提醒下,大家應(yīng)該先了解 AQS,再來看這篇文章。

tryAcquire 調(diào)用的是 nonfairTryAcquire 方法嘗試去獲取同步狀態(tài),接下來我們看 Sync 類中具體做了哪些實(shí)現(xiàn)。

2.3 Sync

abstract static class Sync extends AbstractQueuedSynchronizer {abstract void lock();// 獨(dú)占模式下嘗試獲取鎖final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();// 獲取同步狀態(tài)int c = getState();// 0 表示無狀態(tài),獨(dú)占鎖模式下可理解為沒有鎖重入if (c == 0) {// 更新同步狀態(tài)值if (compareAndSetState(0, acquires)) {// 獨(dú)占式設(shè)置當(dāng)前線程setExclusiveOwnerThread(current);return true;}}// 設(shè)置獨(dú)占鎖可重入else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");// 重新設(shè)置修改后同步的狀態(tài)值setState(nextc);return true;}// 獲取失敗return false;}// 獨(dú)占模式下嘗試釋放鎖protected final boolean tryRelease(int releases) {// 減小同步狀態(tài)int c = getState() - releases;// 不是當(dāng)前線程拋出異常if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;// 如果同步狀態(tài)只為 0,表示可以釋放資源,后面的線程可以嘗試去獲取鎖if (c == 0) {free = true;setExclusiveOwnerThread(null);}// 更新減小后的同步狀態(tài)值setState(c);return free;}}

上面并沒有把 Sync 中的所有源碼貼出來,這些源碼已經(jīng)足夠我們說明問題了。

首先來看 nonfairTryAcquire 方法,該方法用于非公平模式下獲取同步狀態(tài),我們知道 ReentrantLock 是支持可重入的鎖,因此要考慮兩種情況,沒有鎖重入與有鎖重入。其實(shí)源碼也很簡單,有鎖重入的情況下只要增加同步狀態(tài)值即可,這里的同步狀態(tài)值可以理解為鎖重入的次數(shù)。等釋放鎖時(shí)將同步狀態(tài)值再逐次減小,當(dāng)減小為 0 時(shí)表示鎖已經(jīng)釋放,這也是 tryRelease 方法中做的事情。

ReentrantLock 獲取鎖的方式是獨(dú)占的,因此釋放鎖的過程,公平與非公平模式下是相同的。因?yàn)榭紤]到重入的情況,只有當(dāng)同步狀態(tài)減小到為 0 時(shí),才返回 true 表示釋放鎖成功。

2.4 FairSync

static final class FairSync extends Sync {final void lock() {acquire(1);}// 嘗試獲取鎖protected final boolean tryAcquire(int acquires) {// 獲取當(dāng)前線程final Thread current = Thread.currentThread();// 獲取同步狀態(tài)值int c = getState();// 在同步狀態(tài)為 0 的情況下并不是所有線程都可以去獲取同步狀態(tài),等待時(shí)間長的線程會(huì)優(yōu)先獲取同步狀態(tài)if (c == 0) {// 如果隊(duì)列中沒有等待時(shí)間比當(dāng)前線程時(shí)間長的線程,更新同步狀態(tài)值if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {// 設(shè)置當(dāng)前線程獨(dú)占該鎖setExclusiveOwnerThread(current);return true;}}// 保證鎖可重入else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0)throw new Error("Maximum lock count exceeded");// 更新同步狀態(tài)值setState(nextc);return true;}return false;}}

公平策略下獲取同步狀態(tài)的過程與非同步狀態(tài)是類似的,只不過是多了一個(gè) hasQueuedPredecessors 過程判斷,這個(gè)方法用于判斷當(dāng)前線程對(duì)應(yīng)的節(jié)點(diǎn)是否還有前驅(qū)節(jié)點(diǎn),如果有則獲取失敗。下面是具體代碼實(shí)現(xiàn):

public final boolean hasQueuedPredecessors() {Node t = tail; // Read fields in reverse initialization orderNode h = head;Node s;// s 表示頭節(jié)點(diǎn)的后繼節(jié)點(diǎn),如果頭節(jié)點(diǎn)的后繼節(jié)點(diǎn)不是當(dāng)前線程的節(jié)點(diǎn),// 表示當(dāng)前線程并非較先加入隊(duì)列的線程,因此不能成功獲取同步狀態(tài)return h != t &&((s = h.next) == null || s.thread != Thread.currentThread());}

非公平策略下釋放同步狀態(tài)與公平策略下相同,上面已經(jīng)概述過了。

jdk1.8 源碼閱讀:https://github.com/zchen96/jdk1.8-source-code-read

參考資料

《JAVA 并發(fā)編程的藝術(shù)》
深入理解AbstractQueuedSynchronizer(一)
Java并發(fā)之AQS詳解

總結(jié)

以上是生活随笔為你收集整理的Java8 ReentrantLock 源码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。