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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java队列加锁_java并发-----浅析ReentrantLock加锁,解锁过程,公平锁非公平锁,AQS入门,CLH同步队列...

發布時間:2024/8/23 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java队列加锁_java并发-----浅析ReentrantLock加锁,解锁过程,公平锁非公平锁,AQS入门,CLH同步队列... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

為什么需要去了解AQS,AQS,AbstractQueuedSynchronizer,即隊列同步器。它是構建鎖或者其他同步組件的基礎框架(如ReentrantLock、ReentrantReadWriteLock、Semaphore等),JUC并發包的作者(Doug Lea)期望它能夠成為實現大部分同步需求的基礎。它是JUC并發包中的核心基礎組件

本文所有源碼基于JDK9

目的:掌握大概的流程/框架

適用人群:初學者想了解些源碼的

不適合:想深入了解的

ReentrantLock-非公平鎖

我們在實際中一定會用到ReentrantLock的lock操作,那么它的實現究竟是怎樣的?我們以重入鎖作為切入點。

1、構建鎖,獲得鎖對象

//鎖的聲明

private final Sync sync;

// 構造鎖,默認非公平

public ReentrantLock() {

sync = new NonfairSync();

}

2、lock方法,調用sync這個鎖對象

public void lock() {

sync.acquire(1);

}

// 來自AQS

public final void acquire(int arg) {

if (!tryAcquire(arg) &&

acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

selfInterrupt();

}

關鍵性的四個方法:

1、tryAcquire:去嘗試獲取鎖,獲取成功則設置鎖狀態并返回true,否則返回false。該方法自定義同步組件自己實現,該方法必須要保證線程安全的獲取同步狀態。

2、addWaiter:如果tryAcquire返回FALSE(獲取同步狀態失敗),則調用該方法將當前線程加入到CLH同步隊列尾部。

3、acquireQueued:當前線程會根據公平性原則來進行阻塞等待(自旋),直到獲取鎖為止;并且返回當前線程在等待過程中有沒有中斷過。

4、selfInterrupt:產生一個中斷。

tryAcquire

/**

* Sync object for non-fair locks

*/

static final class NonfairSync extends Sync {

private static final long serialVersionUID = 7316153563782823691L;

protected final boolean tryAcquire(int acquires) {

return nonfairTryAcquire(acquires);

}

}

加鎖,是通過NonfairSync的這個方法實現的,但是NofairSync并沒有它的實際代碼。真正實現的是它的父類Sync。

nonfairTryAcquire(int acquires)

final boolean nonfairTryAcquire(int acquires) {

final Thread current = Thread.currentThread();

int c = getState();

if (c == 0) {

if (compareAndSetState(0, acquires)) {

setExclusiveOwnerThread(current);

return true;

}

}

else if (current == getExclusiveOwnerThread()) {

int nextc = c + acquires;

if (nextc < 0) // overflow

throw new Error("Maximum lock count exceeded");

setState(nextc);

return true;

}

return false;

}

getState(),這個方法在AQS中,用戶獲取鎖的狀態值。

/**

* Returns the current value of synchronization state.

* This operation has memory semantics of a {@code volatile} read.

*@return current state value

*/

protected final int getState() {

return state;

}

下面來解釋一下state在獨占鎖的作用是什么,結合下nonfairTryAcquire(int acquires)。

首先第一個if條件,state = 0 的時候代表著,當前鎖沒有被某個線程占用,然后通過CAS操作設置線程的狀態為值1,并且把當前線程設置為獨占鎖的擁有者。

第二個if,當state!=0的時候,代表這個鎖已經被別的線程占著了,就判斷,這個是不是這個鎖的擁有者,如果是的話,鎖state就+1,這就巧妙地設置了重入鎖的。每次+1的機制

CLH同步隊列

在介紹addWaiter前,先來看一下CLH同步隊列,就看著大佬們在說CLH但是少有人說他是啥,我解釋一下啊

The wait queue is a variant of a “CLH” (Craig, Landin, and Hagersten) lock queue. CLH locks are normally used for spinlocks

1、名稱:CLH 由三個人名字組成 (Craig, Landin, and Hagersten)

2、基本數據結構:基于FIFO雙端鏈表

3、用途:用于等待資源釋放的隊列。也就是等待鎖釋放的隊列

注意:由于筆者是初學者,覺得CLH隊列中的等待狀態轉換略微復雜,故意跳過。只看它們的方法,并沒有特別深入

addWaiter

/**

* Creates and enqueues node for current thread and given mode.

*

*@param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared

*@return the new node

*/

private Node addWaiter(Node mode) {

Node node = new Node(mode);

for (;;) {

Node oldTail = tail;

if (oldTail != null) {

node.setPrevRelaxed(oldTail);

if (compareAndSetTail(oldTail, node)) {

oldTail.next = node;

return node;

}

} else {

initializeSyncQueue();

}

}

}

與JDK8不同的地方是,addWaiter直接使用自旋(無限循環)去完成入隊的操作,而不是調用enq,實現的內容和enq差不多。下面給出enq的代碼

/**

* Inserts node into queue, initializing if necessary. See picture above.

*@param node the node to insert

*@return node's predecessor

*/

private Node enq(Node node) {

for (;;) {

Node oldTail = tail;

if (oldTail != null) {

node.setPrevRelaxed(oldTail);

if (compareAndSetTail(oldTail, node)) {

oldTail.next = node;

return oldTail;

}

} else {

initializeSyncQueue();

}

}

}

注意addWaiter的返回值,是新加入的節點下面有用

acquireQueued

final boolean acquireQueued(final Node node, int arg) {

try {

// 中斷標識

boolean interrupted = false;

// 自旋

for (;;) {

final Node p = node.predecessor();

// 如果當前線程節點的前驅節點是頭結點,并且嘗試獲得鎖成功

if (p == head && tryAcquire(arg)) {

setHead(node);

p.next = null; // help GC

return interrupted;

}

// 如果獲取鎖失敗了,就進入掛起。

if (shouldParkAfterFailedAcquire(p, node) &&

parkAndCheckInterrupt())

interrupted = true;

}

} catch (Throwable t) {

cancelAcquire(node);

throw t;

}

}

關于掛起和喚醒,就先不看了,主要還是理解下流程。過多的細節會拖慢新手的學習之路,一定要記住這個!!

RenentrantLock-公平鎖

protected final boolean tryAcquire(int acquires) {

final Thread current = Thread.currentThread();

int c = getState();

if (c == 0) {

if (!hasQueuedPredecessors() &&

compareAndSetState(0, acquires)) {

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;

}

}

公平鎖和非公平鎖在于第一個判斷條件中的,hasQueuedPredecessors()這個方法。

public final boolean hasQueuedPredecessors() {

Node t = tail;

Node h = head;

Node s;

// 頭節點不是尾節點

// 第一個節點不為空

// 當前節點是頭節點

return h != t &&

((s = h.next) == null || s.thread != Thread.currentThread());

}

從源碼我們可以驗證,公平和非公平的標準是否是按照隊列的順序進行鎖的獲取的原理。

小結:

到這里,加鎖基本上就是結束了,我們忽略了掛起和喚醒這種復雜的操作。加鎖的操作中,阻塞隊列是由AQS使用CLH進行維護的。ReentrantLock,的同步操作主要還是依賴于AQS。

釋放鎖

public void unlock() {

sync.release(1);

}

public final boolean release(int arg) {

if (tryRelease(arg)) {

Node h = head;

if (h != null && h.waitStatus != 0)

unparkSuccessor(h);

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;

}

調用Sync的tryRelease,減小state值,然后uppark進行喚醒下一個節點PS,筆者忽略了喚醒的操作。

總結

本文針對和我一樣剛入門不久的新手,從整體上以重入鎖的加鎖和解鎖,去了解了一些AQS的CLH隊列的基本內容,未涉及深層次,順便看了下公平和非公平的實現。算作是一種了解把,個人感覺太細節的東西,新手看了也沒用。還是從宏觀上把握把握,會用,然后了解點源碼,方便以后再來學習。

總結

以上是生活随笔為你收集整理的java队列加锁_java并发-----浅析ReentrantLock加锁,解锁过程,公平锁非公平锁,AQS入门,CLH同步队列...的全部內容,希望文章能夠幫你解決所遇到的問題。

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