【Java】重入鎖 實現原理
ReentrantLock 是java繼synchronized關鍵字之後新出的執行緒鎖,今天看了看實現原始碼。主要是通過自旋來實現的。使用自旋的基本思路就是為所有的執行緒構建一個node,連成一個佇列,然後每一個node都輪詢前驅節點,如果前驅已經釋放鎖了,那麼當前階段就可以獲取鎖,否則就繼續迴圈。
之前瞭解過使用ThreadLocal機制實現的自旋鎖,但是reentrant lock應該是另一種。
這是加鎖方法,先進行一次快速加鎖,失敗了再進行常規加鎖。快速加鎖的情景指的是當前沒有鎖,所以直接CAS原子操作看看能不能獲取,也就是if塊裡的操作,如果沒有成功,常規獲取,也就是acquire操作。/** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
首先執行一次tryAcquire()嘗試獲取,分為公平和非公平,這裡就只看非公平的情況吧
首先獲取當前執行緒,然後得到鎖此時的state,如果state是0,說明可以爭取鎖,CAS一下,否則說明鎖被用了,但是如果用鎖的就是當前執行緒,就把state加1,獲取成功,否則就獲取失敗。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; }
一旦tryAcquire()返回了false,說明獲取鎖失敗了,就必須進入等待佇列中,所以會執行後面的acquireQueued(addWaiter)方法。先看addWaiter
addWaiter所做的是把當前執行緒加入到等待佇列中,也就是把鎖的tail變數設定為當前執行緒,也是先快設定一次,也就是一次CAS,如果成功就返回,否則就執行一次常規加入,即enq操作private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure Node pred = tail; if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); return node; }
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
enq就是典型的自旋+CAS的實現,因為CAS控制併發是非阻塞的,所以如果一定要讓操作執行,必須把CAS放入迴圈內。所以enq就是一個while迴圈,不斷檢測CAS是否成功
一旦加入隊列了,剩下的就是執行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);
}
}
可以看到,確實有一個迴圈,不斷檢測前驅節點,如果前驅是head(這是一個dump頭節點),說明自己已經是真正的頭節點了,可以互獲鎖了,就會持續執行tryAcquire去競爭。這個佇列的消費是直接把消費的節點從佇列刪除,而之前部落格的CLH是通過節點的一個狀態字來檢測的。
可以看到,整個重入鎖就是通過自旋+CAS來實現的
獲取鎖的大致過程如下:
執行一次CAS獲取,若不成功則
執行一次tryAcquire獲取,若不成功則
把當前節點加入到佇列,也是先CAS加入一次,不成功再自旋
自旋檢測前驅是否釋放鎖,並嘗試獲取
與自旋鎖相對應的概念是互斥鎖,等待時是通過讓執行緒睡眠實現的。自旋鎖適用於佔用鎖時間比較短的場景,這樣執行緒狀態轉換的開銷相比較與cpu迴圈,代價會變大,但是隨著鎖獲取時間的增長,cpu的代價會越來越大
相關推薦
【Java】重入鎖 實現原理
ReentrantLock 是java繼synchronized關鍵字之後新出的執行緒鎖,今天看了看實現原始碼。主要是通過自旋來實現的。使用自旋的基本思路就是為所有的執行緒構建一個node,連成一個佇列,然後每一個node都輪詢前驅節點,如果前驅已經釋放鎖了,那麼當前階段就
java可重入鎖ReentrantLock原理
ReentrantLock JUC中ReentrantLock實現了獨佔模式重入鎖,對於可重入,此類的註釋是這樣的: 當一個執行緒鎖住ReentrantLock,但是沒有解鎖,這個執行緒再執行加鎖方法會返回成功,並獲得這把鎖 Reentrant
【Java】手把手理解CAS實現原理
先來看看概念,【CAS】 全稱“CompareAndSwap”,中文翻譯即“比較並替換”。 定義:CAS操作包含三個運算元 —— 記憶體位置(V),期望值(A),和新值(B)。 如果記憶體位置的值與期望值匹配,那麼處理器會自動將該位置值更新為新值。否則, 處理器不作任何操作。無論哪種情況,它都會在C
java可重入鎖(ReentrantLock)的實現原理
聲明 其他人 不用 vol 喚醒 ola 是不是 結束 真的 前言相信學過java的人都知道 synchronized 這個關鍵詞,也知道它用於控制多線程對並發資源的安全訪問,興許,你還用過Lock相關的功能,但你可能從來沒有想過java中的鎖底層的機制是怎麽實現的。如果真
輕鬆學習java可重入鎖(ReentrantLock)的實現原理
前言 相信學過java的人都知道 synchronized 這個關鍵詞,也知道它用於控制多執行緒對併發資源的安全訪問,興許,你還用過Lock相關的功能,但你可能從來沒有想過java中的鎖底層的機制是怎麼實現的。如果真是這樣,而且你有興趣瞭解,今天我將帶領你輕鬆
漫畫圖解java可重入鎖(ReentrantLock)的實現原理
前言 相信學過java的人都知道 synchronized 這個關鍵詞,也知道它用於控制多執行緒對併發資源的安全訪問,興許,你還用過Lock相關的功能,但你可能從來沒有想過java中的鎖底層的機制是怎麼實現的。如果真是這樣,而且你有興趣瞭解,今天我將帶領你輕鬆的學習下j
【Java】使用Atomic變數實現鎖
Atomic原子操作 Java從JDK1.5開始提供了java.util.concurrent.atomic包,方便程式設計師在多執行緒環境下,無鎖的進行原子操作。原子變數的底層使用了處理器提供的原子指令,但是不同的CPU架構可能提供的原子指令不一樣,也有可能需要某種形式的內部鎖,所
【Java】Swing+IO流實現一個簡單的文件加密程序
als oncommand override fault 源文件 abs directory imp select EncrytService package com.my.service; import java.io.File; import java
【Java】Swing+IO流實現一個簡單的文件加密程序(較完整版)
move 初始 baidu images 文件選擇器 while login 一個 ktr 留著參考 beans package com.my.bean; import java.io.Serializable; public class
淺談BloomFilter【上】基本概念和實現原理
pty 是否 的人 它的 構建 網絡爬蟲 ace head filters ? ??在日常生活中。包括在設計計算機軟件時,我們常常要推斷一個元素是否在一個集合中。
【Java】重載(Overload)與重寫(Override)
{} 方法 ide jump exce 上一條 內部 https pre 方法的語法 修飾符 返回值類型 方法名(參數類型 參數名){ ... 方法體 ... return 返回值; } 重載(overload) /** * 重載
【spring】Spring aop的實現原理
本文轉載自https://www.cnblogs.com/lcngu/p/5339555.html。 Spring aop的實現原理 簡介 前段時間寫的java設計模式--代理模式,最近在看Spring Aop的時候,覺得於代理模式應該有密切的聯絡,於是決定了解下Sprin
【Java】模擬Sping,實現其IOC和AOP核心(一)
在這裡我要實現的是Spring的IOC和AOP的核心,而且有關IOC的實現,註解+XML能混合使用! 參考資料: IOC:控制反轉(Inversion of Control,縮寫為IoC),是面向物件程式設計中的一種設計原則,可以用來減低計算機程式碼之間的耦合度。其中最常見的方式叫做依賴注入(D
Java可重入鎖ReentrantLock
在JDK1.5之前,我們設計程式進行執行緒之間通訊時必須使用同步鎖,獲得同步鎖必須使用同步關鍵字synchronized(lock)的形式。JDK1.5提供了執行緒同步相關的包java.util.concurrent,引入了可重入鎖ReentrantLock,使用起來很方便並且提高了程式碼執
Java可重入鎖與其釋放
在學習Java多執行緒相關的知識時,通常都會涉及鎖這個概念,常見的synchronized、Lock均為可重入鎖。為了更好的理解可重入鎖,需要先理解一下幾個問題: 1、誰持有了鎖? 2、鎖的物件是誰? 3、可重入鎖的可重入是什麼意思? 一、synchronized關鍵字
java基礎總結(二十九)--Java不可重入鎖和可重入鎖理解
來自:https://blog.csdn.net/u012545728/article/details/80843595 最近正在閱讀Java ReentrantLock原始碼,始終對可重入和不可重入概念理解不透徹,進行學習後記錄在這裡。 基礎知識 Java多執行緒的wai
【JavaScript】Function.prototype.bind實現原理
前言:ECMAscript5中的bind()是ES6中箭頭函式繫結this的基礎。它是的實現原理是怎樣的呢? 一、回顧bind的用法 const nick = { name:'nick'
【Java】基於jsoup爬蟲實現(從智聯獲取工作資訊)
這幾天在學習Java解析xml,突然想到Dom能不能解析html,結果試了半天行不通,然後就去查了一些資料,發現很多人都在用Jsoup解析html檔案,然後研究了一下,寫了一個簡單的例項,感覺還有很多地方需要潤色,在這裡分享一下我的例項,歡迎交流指教!後續想通過Java把資料匯入到Excel或者
【Java】模擬Sping,實現其IOC和AOP核心(二)
接著上一篇,在上一篇完成了有關IOC的註解實現,這一篇用XML的方式實現IOC,並且完成AOP。 簡易的IOC框圖 註解的方式實現了左邊的分支,那麼就剩下右邊的XML分支: XmlContext:這個類是也是AbstractApplicationContext的子類,和AnnotationContext
【JAVA】Srping和JDBC實現資料庫操作
前言 建立資料庫 首先建立我們的資料庫(這裡我使用的是Mysql),為了演示方便,我這裡簡單的建立一個spring資料庫,然後資料庫有一個user使用者表: 建立一個名為spring的資料庫。 建立一個名為user的資料表,表包括id、email、name