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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java并发编程-ReentrantLock源码分析

發布時間:2023/12/10 java 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java并发编程-ReentrantLock源码分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、前言

在分析了?AbstractQueuedSynchronier?源碼后,接著分析ReentrantLock源碼,其實在?AbstractQueuedSynchronizer?的分析中,已經提到過ReentrantLock,ReentrantLock表示下面具體分析ReentrantLock源碼。

二、ReentrantLock數據結構

ReentrantLock的底層是借助AbstractQueuedSynchronizer實現,所以其數據結構依附于AbstractQueuedSynchronizer的數據結構,關于AQS的數據結構,在前一篇已經介紹過,不再累贅。

三、ReentrantLock源碼分析

3.1 類的繼承關系

public class ReentrantLock implements Lock, java.io.Serializable 說明:ReentrantLock實現了Lock接口,Lock接口中定義了lock與unlock相關操作,并且還存在newCondition方法,表示生成一個條件。

3.2 類的內部類

ReentrantLock總共有三個內部類,并且三個內部類時緊密相關的,下面先看三個類的關系。

說明:ReentrantLock類內部總共存在Sync、NonfairSync、FairSync三個類,NonfairSync與FairSync類繼承自Sync類,Sync類繼承自AbstractQueuedSynchronizer抽象類。下面逐個進行分析。

1. Sync類

Sync類的源碼如下

abstract static class Sync extends AbstractQueuedSynchronizer {// 序列號private static final long serialVersionUID = -5179523762034025860L;// 獲取鎖abstract void lock();// 非公平方式獲取final boolean nonfairTryAcquire(int acquires) {// 當前線程final Thread current = Thread.currentThread();// 獲取狀態int c = getState();if (c == 0) { // 表示沒有線程正在競爭該鎖if (compareAndSetState(0, acquires)) { // 比較并設置狀態成功,狀態0表示鎖沒有被占用// 設置當前線程獨占 setExclusiveOwnerThread(current); return true; // 成功 }}else if (current == getExclusiveOwnerThread()) { // 當前線程擁有該鎖int nextc = c + acquires; // 增加重入次數if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");// 設置狀態 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; }// 判斷資源是否被當前線程占有protected final boolean isHeldExclusively() {// While we must in general read state before owner,// we don't need to do so to check if current thread is ownerreturn getExclusiveOwnerThread() == Thread.currentThread();}// 新生一個條件final ConditionObject newCondition() {return new ConditionObject();}// Methods relayed from outer class// 返回資源的占用線程final Thread getOwner() { return getState() == 0 ? null : getExclusiveOwnerThread();}// 返回狀態final int getHoldCount() { return isHeldExclusively() ? getState() : 0;}// 資源是否被占用final boolean isLocked() { return getState() != 0;}/*** Reconstitutes the instance from a stream (that is, deserializes it).*/// 自定義反序列化邏輯private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();setState(0); // reset to unlocked state }}

說明:Sync類存在如下方法和作用如下。

2. NonfairSync類

NonfairSync類繼承了Sync類,表示采用非公平策略獲取鎖,其實現了Sync類中抽象的lock方法,源碼如下。

// 非公平鎖static final class NonfairSync extends Sync {// 版本號private static final long serialVersionUID = 7316153563782823691L;// 獲得鎖final void lock() {if (compareAndSetState(0, 1)) // 比較并設置狀態成功,狀態0表示鎖沒有被占用// 把當前線程設置獨占了鎖 setExclusiveOwnerThread(Thread.currentThread());else // 鎖已經被占用,或者set失敗// 以獨占模式獲取對象,忽略中斷acquire(1); }protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}}

說明:從lock方法的源碼可知,每一次都嘗試獲取鎖,而并不會按照公平等待的原則進行等待,讓等待時間最久的線程獲得鎖。

3. FairSyn類

FairSync類也繼承了Sync類,表示采用公平策略獲取鎖,其實現了Sync類中的抽象lock方法,源碼如下。

// 公平鎖static final class FairSync extends Sync {// 版本序列化private static final long serialVersionUID = -3000897897090466540L;final void lock() {// 以獨占模式獲取對象,忽略中斷acquire(1);}/*** Fair version of tryAcquire. Don't grant access unless* recursive call or no waiters or is first.*/// 嘗試公平獲取鎖protected final boolean tryAcquire(int acquires) {// 獲取當前線程final Thread current = Thread.currentThread();// 獲取狀態int c = getState();if (c == 0) { // 狀態為0if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) { // 不存在已經等待更久的線程并且比較并且設置狀態成功// 設置當前線程獨占 setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) { // 狀態不為0,即資源已經被線程占據// 下一個狀態int nextc = c + acquires;if (nextc < 0) // 超過了int的表示范圍throw new Error("Maximum lock count exceeded");// 設置狀態 setState(nextc);return true;}return false;}}

說明:跟蹤lock方法的源碼可知,當資源空閑時,它總是會先判斷sync隊列(AbstractQueuedSynchronizer中的數據結構)是否有等待時間更長的線程,如果存在,則將該線程加入到等待隊列的尾部,實現了公平獲取原則。其中,FairSync類的lock的方法調用如下,只給出了主要的方法。

說明:可以看出只要資源被其他線程占用,該線程就會添加到sync queue中的尾部,而不會先嘗試獲取資源。這也是和Nonfair最大的區別,Nonfair每一次都會嘗試去獲取資源,如果此時該資源恰好被釋放,則會被當前線程獲取,這就造成了不公平的現象,當獲取不成功,再加入隊列尾部。

3.3 類的屬性

public class ReentrantLock implements Lock, java.io.Serializable {// 序列號private static final long serialVersionUID = 7373984872572414699L; // 同步隊列private final Sync sync; }

說明:ReentrantLock類的sync非常重要,對ReentrantLock類的操作大部分都直接轉化為對Sync和AbstractQueuedSynchronizer類的操作。

3.4 類的構造函數

1. ReentrantLock()型構造函數

public ReentrantLock() {// 默認非公平策略sync = new NonfairSync();}

說明:可以看到默認是采用的非公平策略獲取鎖。

2. ReentrantLock(boolean)型構造函數

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

說明:可以傳遞參數確定采用公平策略或者是非公平策略,參數為true表示公平策略,否則,采用非公平策略。

3.5 類的核心函數分析

通過分析ReentrantLock的源碼,可知對其操作都轉化為對Sync對象的操作,由于Sync繼承了AQS,所以基本上都可以轉化為對AQS的操作。如將ReentrantLock的lock函數轉化為對Sync的lock函數的調用,而具體會根據采用的策略(如公平策略或者非公平策略)的不同而調用到Sync的不同子類。

所以可知,在ReentrantLock的背后,是AQS對其服務提供了支持,由于之前我們分析AQS的核心源碼,遂不再累贅。下面還是通過例子來更進一步分析源碼。

四、示例分析

4.1 公平鎖

package com.hust.grid.leesf.abstractqueuedsynchronizer;import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;class MyThread extends Thread {private Lock lock;public MyThread(String name, Lock lock) {super(name);this.lock = lock;}public void run () {lock.lock();try {System.out.println(Thread.currentThread() + " running");try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}} finally {lock.unlock();}} }public class AbstractQueuedSynchonizerDemo {public static void main(String[] args) throws InterruptedException {Lock lock = new ReentrantLock(true);MyThread t1 = new MyThread("t1", lock); MyThread t2 = new MyThread("t2", lock);MyThread t3 = new MyThread("t3", lock);t1.start();t2.start(); t3.start();} } 運行結果(某一次): Thread[t1,5,main] running Thread[t2,5,main] running Thread[t3,5,main] running

說明:該示例使用的是公平策略,由結果可知,可能會存在如下一種時序。

說明:首先,t1線程的lock操作 -> t2線程的lock操作 -> t3線程的lock操作 -> t1線程的unlock操作 -> t2線程的unlock操作 -> t3線程的unlock操作。根據這個時序圖來進一步分析源碼的工作流程。

① t1線程執行lock.lock,下圖給出了方法調用中的主要方法。

說明:由調用流程可知,t1線程成功獲取了資源,可以繼續執行。

② t2線程執行lock.lock,下圖給出了方法調用中的主要方法。

說明:由上圖可知,最后的結果是t2線程會被禁止,因為調用了LockSupport.park。

③ t3線程執行lock.lock,下圖給出了方法調用中的主要方法。

說明:由上圖可知,最后的結果是t3線程會被禁止,因為調用了LockSupport.park。

④ t1線程調用了lock.unlock,下圖給出了方法調用中的主要方法。

說明:如上圖所示,最后,head的狀態會變為0,t2線程會被unpark,即t2線程可以繼續運行。此時t3線程還是被禁止。

⑤ t2獲得cpu資源,繼續運行,由于t2之前被park了,現在需要恢復之前的狀態,下圖給出了方法調用中的主要方法。

說明:在setHead函數中會將head設置為之前head的下一個結點,并且將pre域與thread域都設置為null,在acquireQueued返回之前,sync queue就只有兩個結點了。

⑥ t2執行lock.unlock,下圖給出了方法調用中的主要方法。

說明:由上圖可知,最終unpark t3線程,讓t3線程可以繼續運行。

⑦ t3線程獲取cpu資源,恢復之前的狀態,繼續運行。

說明:最終達到的狀態是sync queue中只剩下了一個結點,并且該節點除了狀態為0外,其余均為null。

⑧ t3執行lock.unlock,下圖給出了方法調用中的主要方法。

說明:最后的狀態和之前的狀態是一樣的,隊列中的一個結點會被GC,最后隊列會為空。

使用公平策略和Condition的情況可以參考上一篇關于AQS的源碼示例分析部分,不再累贅。

五、總結

再掌握了AQS后,再來分析ReentrantLock的源碼,就會非常簡單,因為ReentrantLock的絕大部分操作都是基于AQS類的。所以,進行分析時要找準方向,就會事半功倍。謝謝各位園友觀看~

總結

以上是生活随笔為你收集整理的Java并发编程-ReentrantLock源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。