摘要:公平鎖阻塞隊列前邊有線程,要去后邊排隊,簡單來說滾后邊等著去。非公平鎖不管是否有線程排隊,先槍鎖基于實現的可重入鎖實現類。
AQS (AbstractQueuedSynchronizer)
底層一個隊列 阻塞隊列 ->
? Abstract:因為它并不知道怎么上鎖。模板方法設計模式即可,暴露出鎖邏輯。
? Queue :線程阻塞隊列 Synchronizer:同步
? CAS + state 完成多線程槍鎖邏輯 Queue 完成搶不到鎖的線程排隊
//獲取鎖public final void acquire(int arg) { if (!tryAcquire(arg) && // 子類判定獲取鎖是否失敗 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 獲取失敗后添加到阻塞隊列 selfInterrupt();}// 子類實現獲取鎖的邏輯,AQS并不知道你怎么用這個state來上鎖protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException();}//釋放鎖的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 boolean tryRelease(int arg) { throw new UnsupportedOperationException();}
子類只需要實現自己的獲取鎖邏輯和釋放鎖邏輯即可,至于排隊阻塞等待、喚醒機制均由AQS來完成。
公平鎖:阻塞隊列前邊有線程,要去后邊排隊,簡單來說 滾后邊等著去。(FIFO) 非公平鎖:不管是否有線程排隊,先槍鎖
基于AQS實現的可重入鎖實現類。
ReentrantLock 是一個可重入的互斥(/獨占)鎖,又稱為“獨占鎖”
重入鎖: 自己可以再次獲取自己的內部的鎖。
state 來表示鎖狀態和重入次數,0無鎖,大于0 重入次數,1為重入1次,也即只加鎖一次
private final Sync sync;//sync,構造方法中初始化,決定使用公平鎖還是非公平鎖的方式獲取鎖。
public ReentrantLock() { // 默認為非公平鎖。 sync = new NonfairSync(); } public ReentrantLock(boolean fair) { // 由fair變量來表明選擇鎖類型 sync = fair ? new FairSync() : new NonfairSync(); }
abstract static class Sync extends AbstractQueuedSynchronizer {}
static final class NonfairSync extends Sync {} 非公平鎖
static final class FairSync extends Sync {} 公平鎖
//非公平鎖 static final class NonfairSync extends Sync { // 由ReentrantLock調用獲取鎖 final void lock() { // 非公平鎖,直接搶鎖,不管有沒有線程排隊 if (compareAndSetState(0, 1)) // 上鎖成功,標識當前線程為獲取鎖的線程 setExclusiveOwnerThread(Thread.currentThread()); else // 搶鎖失敗,進入AQS的標準獲取鎖流程 acquire(1); } protected final boolean tryAcquire(int acquires) { // 使用父類提供的獲取非公平鎖的方法來獲取鎖 return nonfairTryAcquire(acquires); } } //公平鎖 static final class FairSync extends Sync { // 由ReentrantLock調用 final void lock() { // 沒有嘗試搶鎖,直接進入AQS標準獲取鎖流程 acquire(1); } // AQS調用,子類自己實現獲取鎖的流程 protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); // 此時有可能正好獲取鎖的線程釋放了鎖,也有可能本身就沒有線程獲取鎖 if (c == 0) { //這里和非公平鎖的區別在于:hasQueuedPredecessors看看隊列中是否有線程正在排隊,沒有的話再通過CAS搶鎖 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; } //需要AQS來將當前線程放入阻塞隊列,然后進行阻塞操作等待喚醒獲取鎖 return false; } } abstract static class Sync extends AbstractQueuedSynchronizer { abstract void lock(); // 非公平鎖標準獲取鎖方法 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()) { // 利用state 進行次數記錄 int nextc = c + acquires; // 如果超過了int表示范圍,表明符號溢出,所以拋出異常 if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc);//state 變量賦值 return true; } //表明需要AQS來將當前線程放入阻塞隊列,然后進行阻塞操作等待喚醒獲取鎖 return false; } // 公平鎖和非公平鎖公用方法,在釋放鎖的時候,并不區分是否公平 protected final boolean tryRelease(int releases) { int c = getState() - releases; // 如果當前線程不是上鎖的那個線程 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; // 不是重入鎖,那么當前線程一定是釋放鎖了,把當前AQS用于保存當前鎖對象的變量ExclusiveOwnerThread設置為null,表明釋放鎖成功 if (c == 0) { free = true; setExclusiveOwnerThread(null); } //此時state全局變量沒有改變,也就意味著在setState之前 //沒有別的線程能夠獲取鎖,保證了以上的操作原子性 setState(c); //釋放鎖成功了,可以去喚醒正在等待鎖的線程 return free; } protected final boolean isHeldExclusively() { return getExclusiveOwnerThread() == Thread.currentThread(); } final ConditionObject newCondition() { return new ConditionObject(); } }
public void lock() {//獲取鎖的操作 // 直接通過sync同步器上鎖 sync.lock();}public void unlock() {//釋放鎖的操作 sync.release(1);}
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/125657.html
摘要:簡介繼續分析源碼,上一篇文章把的分析完畢。本文開始分析簡單的介紹一下。存儲的元素是無序的并且允許使用空的元素。 1.簡介 繼續分析源碼,上一篇文章把HashMap的分析完畢。本文開始分析HashSet簡單的介紹一下。 HashSet是一個無重復元素集合,內部使用HashMap實現,所以HashMap的特征耶繼承了下來。存儲的元素是無序的并且HashSet允許使用空的元素。 HashSe...
摘要:則使用了拉鏈式的散列算法,并在中引入了紅黑樹優化過長的鏈表。如果大家對紅黑樹感興趣,可以閱讀我的另一篇文章紅黑樹詳細分析。構造方法構造方法分析的構造方法不多,只有四個。 1.概述 本篇文章我們來聊聊大家日常開發中常用的一個集合類 - HashMap。HashMap 最早出現在 JDK 1.2中,底層基于散列算法實現。HashMap 允許 null 鍵和 null 值,在計算哈鍵的哈希值...
摘要:所謂拉鏈法就是將鏈表和數組相結合。若遇到哈希沖突,則將沖突的值加到鏈表中即可。在編寫程序中,要盡量避免。 目錄: 0-1. 簡介 0-2. 內部結構分析 0-2-1. JDK18之前 0-2-2. JDK18之后 0-3. LinkedList源碼分析 0-3-1. 構造方法 0-3-2. put方法 0-3-3. get方法 0-3-4. resize方法 ...
摘要:唐老師,回答道讀源碼是要建立在你的基礎經驗足夠的情況下。除了自己去閱讀源碼之外,比如學習某個類的時候,可以專門結合一些優質的博客針對性的對比學習,并查漏補缺。制定源碼學習計劃。多調試,跟蹤源碼。如若有好的學習方法,可以留言一起交流學習。 序言:目前看一看源碼,來提升自己的技術實力。同時現在好多面試官都喜歡問源碼,問你是否讀過JDK源碼等等? 針對如何閱讀源碼,也請教了我的老師。下面就先...
閱讀 3735·2023-01-11 11:02
閱讀 4244·2023-01-11 11:02
閱讀 3050·2023-01-11 11:02
閱讀 5180·2023-01-11 11:02
閱讀 4737·2023-01-11 11:02
閱讀 5534·2023-01-11 11:02
閱讀 5313·2023-01-11 11:02
閱讀 3990·2023-01-11 11:02