ReentrantLock 公平鎖原始碼 第2篇
Reentrant 2
前兩篇寫完了後我自己研究了下,還有有很多疑惑和問題,這篇就繼續以自問自答的方式寫
如果沒看過第1篇的可以先看看那個http://www.cnblogs.com/sunankang/p/16458795.html
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
進入 acquireQueued
方法
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { //這個屬性的作用是啥??? boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
第一個問題
interrupted這個變數的作用
private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
在 parkAndCheckInterrupt
方法中最後return的是這個執行緒是否被打斷,它的作用是啥?
先來回顧 interrupt()
, interrupted()
和 isInterrupted()
三者區別,長得很像,注意區分
interrupt()
的作用是中斷執行緒,如果被中斷的執行緒處於阻塞狀態下,例如呼叫 wait()
, join()
sleep()
,則丟擲異常,否則只是設定一箇中斷標記為true, 注意:僅僅是設定中斷狀態為true ,並不會去 "中斷" 執行緒
interrupted()
獲取執行緒的中斷狀態並且清空中斷狀態(將中斷狀態設定為false)
isInterrupted()
獲取執行緒的中斷狀態並不會清除中斷狀態
呼叫 interrupt 會使park方法立即結束,可以理解為喚醒
繼續程式碼,看這個變數最後到了哪裡
情況1 沒有被打斷過
假設執行緒沒有被中斷過,那麼 parkAndCheckInterrupt
返回就是false
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
那麼不進入 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
這個if,獲取到鎖後返回false,回到 acquire
方法
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
因為false,所以不進入 selfInterrupt()
,方法結束
情況2 park或準備park,被喚醒後直接獲取到了鎖
先證明一下打斷是會喚醒park中的執行緒的
我就再重複粘一下程式碼了,方便看
private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
那麼返回的就是true,回到上級 acquireQueued
方法
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } //返回到這裡 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
因為返回true,所以進入 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
將interrupted返回true
假設迴圈獲取到鎖,那麼再返回上一級 acquire()
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
那麼進入 selfInterrupt()
static void selfInterrupt() { Thread.currentThread().interrupt(); }
是不是有點疑惑?我如果沒有呼叫過 interrupt()
那ReentrantLock就不做任何操作,我如果呼叫了,那它再給我呼叫一次 ???? 還有情況3
情況3 park或準備park,被喚醒後沒有獲取到鎖
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } //假設在呼叫shouldParkAfterFailedAcquire成功後,馬上就要呼叫parkAndCheckInterrupt 時間片用完了 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
那麼這個時候interrupted屬性就有用了
首先要知道一點, 一個被中斷的執行緒是無法park的 ,除非清除了中斷狀態,即設定為將中斷狀態設定為false, 口說無憑,直接上圖
第二張圖還是在park狀態,證明了被打斷的執行緒是無法park的,除非將它中斷狀態設定為false
那麼回到程式碼中就能知道這個的作用
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
如果執行緒被打斷喚醒,還是在 for(;;)
中,還是去獲取鎖,假設沒有獲取到呢?那麼就一直在for迴圈中嘎嘎跑,因為執行緒的狀態是被中斷的,無法再次park了
private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
那麼現在懂了最後的 Thread.interrupted()
作用了嗎,就是將中斷狀態設定回false,好讓執行緒沒有獲取到鎖繼續park
那這時候可能就問了:那你ReentrantLock把中斷狀態給我清空了,我自己如果有需要根據中斷狀態來判斷的程式碼咋辦啊?
好,咱們從park先被打斷來捋一下
private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
因為被打斷,執行緒醒來,執行 Thread.interrupted()
並清空中斷狀態,返回true
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) //進入這裡 interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
因為返回的是true,所以進入 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
的程式碼塊,將 interrupted
屬性設定為true
那麼 for(;;)
迴圈再來一次,如果沒有獲取到鎖.繼續park,直到被喚醒,走 tryAcquire()
獲取到為止,那麼此時 interrupted
變數就為true了
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
那麼退出 acquireQueued()
方法回到 acquire()
中,因為 acquireQueued()
返回的是true,所以進入 selfInterrupt()
static void selfInterrupt() { Thread.currentThread().interrupt(); }
所以懂了嗎?
- 執行緒池底層原理詳解與原始碼分析
- 30分鐘掌握 Webpack
- 線性迴歸大結局(嶺(Ridge)、 Lasso迴歸原理、公式推導),你想要的這裡都有
- 【前端必會】webpack loader 到底是什麼
- 中心化決議管理——雲端分析
- HashMap底層原理及jdk1.8原始碼解讀
- 詳解JS中 call 方法的實現
- 列印 Logger 日誌時,需不需要再封裝一下工具類?
- 初識設計模式 - 代理模式
- 密碼學奇妙之旅、01 CFB密文反饋模式、AES標準、Golang程式碼
- Springboot之 Mybatis 多資料來源實現
- CAS核心思想、底層實現
- 面試突擊86:SpringBoot 事務不回滾?怎麼解決?
- 基於electron vue element構建專案模板之【打包篇】
- MiniWord .NET Word模板引擎,藉由Word模板和資料簡單、快速生成檔案。
- 認識執行緒,初始併發
- 1-VSCode搭建GD32開發環境
- 初識設計模式 - 原型模式
- 執行緒安全問題的產生條件、解決方式
- 2>&1到底是什麼意思?