Java中的ReentrantLock實(shí)現(xiàn)原理及代碼演示
介紹
互斥鎖 實(shí)現(xiàn)Lock接口 并且最好在 finally 塊中釋放
公平鎖 非公平鎖 如果已經(jīng)進(jìn)入隊(duì)列,鏈表里面的線程是先進(jìn)先出,如果已經(jīng)釋放了鎖,在搶占鎖時(shí),鏈表里面的頭結(jié)點(diǎn)和還沒有入隊(duì)列的線程搶鎖
使用代碼
public class Test { public static void main(String[] args) { Task task = new Task(); for(int i = 0; i < 10; i++) { new Thread(task).start(); } } } class Task implements Runnable { Lock lock = new ReentrantLock(); @Override public void run() { lock.lock(); try { Thread.sleep(1000); System.out.println("業(yè)務(wù)代碼"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
實(shí)現(xiàn)原理
采用AQS+CAS+LockSupport用來阻塞和喚醒線程)
ReentrantLock有三類內(nèi)部類,實(shí)現(xiàn)都在其內(nèi)部類Sync中,默認(rèn)是使用非公平鎖NonFairSync。
非公平鎖可提高效率,在可重入鎖時(shí)可以減少線程切換開銷??梢酝ㄟ^構(gòu)造方法切換公平和非公平
Sync父類AbstractQueuedSynchronize(AQS) 此處的鎖具備synchronized功能,即可以阻塞一個(gè)線程。
為了實(shí)現(xiàn)一把具有阻塞或喚醒功能的鎖,需要幾個(gè)核心要素
(1) state變量,標(biāo)記鎖狀態(tài)。至少有兩個(gè)值0/1。對(duì)state的操作,使用CAS保證線程安全
- AbstractQueuedSynchronizer類里有變量private volatile int state 記錄鎖狀態(tài)
- state=0,沒有線程持有鎖,exclusiveOwnerThread=null
- state=1,有一個(gè)線程持有鎖,exclusiveOwnerThread=該線程
- state > 1,說明該線程重入了該鎖,等于幾就重入了幾次
(2) 需要記錄當(dāng)前是哪個(gè)線程持有鎖
- AbstractOwnableSynchronizer里面有變量private transient Thread exclusiveOwnerThread;記錄鎖持有的線程
(3) 需要底層支持對(duì)一個(gè)線程進(jìn)行阻塞或喚醒操作
- public native void unpark(Object thread); // 喚醒某一個(gè)線程
- public native void park(boolean isAbsolute, long time); // 阻塞某一個(gè)線程 實(shí)現(xiàn)一個(gè)線程對(duì)另外一個(gè)線程的精準(zhǔn)喚醒
- 一般使用LockSupport的工具類,對(duì)上面兩個(gè)方法進(jìn)行了封裝
- 當(dāng)前線程中調(diào)用park()就會(huì)被阻塞 另一個(gè)線程調(diào)用unpark(Thread t)傳入被阻塞線程就可喚醒阻塞在park()地方的線程
(4) 需要有一個(gè)隊(duì)列維護(hù)所有阻塞的線程。這個(gè)隊(duì)列也必須是線程安全的無鎖隊(duì)列,也需要使用CAS對(duì)隊(duì)列進(jìn)行增加或刪除
public abstract class AbstractQueuedSynchronizer { // 雙向鏈表 static final class Node { volatile Thread thread; // 每個(gè)Node對(duì)應(yīng)一個(gè)被阻塞的線程 volatile Node prev; // 前一個(gè) volatile Node next; // 后一個(gè) } private transient volatile Node head; // 頭 private transient volatile Node tail; // 尾 }
阻塞隊(duì)列是整個(gè)AQS核心中的核心。如下圖所示,head指向雙向鏈表頭部,tail指向雙向鏈表尾部。入隊(duì)就是把新的Node加到tail后面,然后對(duì)tail進(jìn)行CAS操作;
出隊(duì)就是對(duì)head進(jìn)行CAS操作,把head向后移一個(gè)位置
初始的時(shí)候,head=tail=NULL;然后,在往隊(duì)列中加入阻塞的線程時(shí),會(huì)新建一個(gè)空的Node,讓head和tail都指向這個(gè)空Node;之后,在后面加入被阻塞的線程對(duì)象。
所以,當(dāng)head=tail的時(shí)候,說明隊(duì)列為空。
到此這篇關(guān)于Java中的ReentrantLock實(shí)現(xiàn)原理及代碼演示的文章就介紹到這了,更多相關(guān)ReentrantLock實(shí)現(xiàn)原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Hibernate延遲加載原理與實(shí)現(xiàn)方法
這篇文章主要介紹了Hibernate延遲加載原理與實(shí)現(xiàn)方法,較為詳細(xì)的分析了Hibernate延遲加載的概念,原理與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-03-03簡(jiǎn)單了解Spring Framework5.0新特性
這篇文章主要介紹了簡(jiǎn)單了解Spring Framework5.0新特性,涉及了核心框架修訂,核心容器更新,使用Kotlin進(jìn)行函數(shù)式編程等幾個(gè)方面的介紹,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11Spring如何重寫內(nèi)置Bean(Controller、Service等)
本文介紹了在Spring Boot工程中處理外部JAR包中Controller方法重寫的需求,通過PostProcessor方式和自定義注解@ExcludeBean兩種方法,解決了在不修改源代碼的情況下重寫接口的問題2025-01-01Java實(shí)現(xiàn)求子數(shù)組和的最大值算法示例
這篇文章主要介紹了Java實(shí)現(xiàn)求子數(shù)組和的最大值算法,涉及Java數(shù)組遍歷、判斷、運(yùn)算等相關(guān)操作技巧,需要的朋友可以參考下2018-02-02Spring MVC4.1服務(wù)器端推送實(shí)現(xiàn)過程解析
這篇文章主要介紹了Spring MVC4.1服務(wù)器端推送實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11IDEA生成patch和使用patch的方法實(shí)現(xiàn)
比如你本地修復(fù)的 bug,需要把增量文件發(fā)給客戶,很多場(chǎng)景下大家都需要手工整理修改的文件,并整理好目錄,這個(gè)很麻煩,那有沒有簡(jiǎn)單的技巧呢?本文主要介紹了IDEA生成patch和使用patch的方法實(shí)現(xiàn),感興趣的可以了解一下2023-08-08