Java中的ReentrantLock解讀
ReentrantLock
ReentantLock 是java中重入鎖的實(shí)現(xiàn),一次只能有一個(gè)線程來持有鎖,包含三個(gè)內(nèi)部類, Sync 、 NonFairSync 、 FairSync 。
1、構(gòu)造函數(shù)
無參構(gòu)造,默認(rèn)使用的是非公平性鎖
public ReentrantLock() { sync = new NonfairSync(); }
有參構(gòu)造, Boolean類型的參數(shù) true:表示公平性鎖 false:非公平性鎖
public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
reentantlock是lock接口的實(shí)現(xiàn)類,即實(shí)現(xiàn)了Lock接口下所有的方法; 獲取鎖的方法lock、trylock、lockintertuptibly加鎖方式以及釋放鎖方法。
2、公平性鎖和非公平性鎖
(1)公平性鎖和非公平性鎖示例
NonFairAndFairDemo類
import java.util.concurrent.locks.ReentrantLock; public class NonFairAndFairDemo implements Runnable { //靜態(tài)變量(線程共享) private static int num = 0; //鎖實(shí)例 private ReentrantLock rtl; public NonFairAndFairDemo(ReentrantLock rtl) { this.rtl = rtl; } @Override public void run() { while (true) { //加鎖 rtl.lock(); num++; System.out.println(Thread.currentThread().getName() + ":" + num); rtl.unlock(); } } }
測(cè)試公平鎖
@Test public void test01() { ReentrantLock reentrantLock = new ReentrantLock(true); Thread threadA = new Thread(new NonFairAndFairDemo(reentrantLock)); threadA.setName("A"); Thread threadB = new Thread(new NonFairAndFairDemo(reentrantLock)); threadB.setName("B"); threadA.start(); threadB.start(); }
執(zhí)行結(jié)果
公平向鎖特征如上:按照線程的訪問順序進(jìn)行獲取。
測(cè)試非公平鎖
@Test public void test02() { ReentrantLock reentrantLock = new ReentrantLock(false); Thread threadA = new Thread(new NonFairAndFairDemo(reentrantLock)); threadA.setName("A"); Thread threadB = new Thread(new NonFairAndFairDemo(reentrantLock)); threadB.setName("B"); threadA.start(); threadB.start(); }
執(zhí)行結(jié)果
非公平性鎖的特點(diǎn),是每個(gè)線程都連續(xù)執(zhí)行多次之后在替換成其他線程執(zhí)行。
(2)公平鎖和非公平鎖的實(shí)現(xiàn)
abstract static class Sync extends AbstractQueuedSynchronizer
公平性鎖和非公平性鎖的父類是 sync , sync 類是 AbstractQueuedSynchronizer 是其子類,AQS是一個(gè)同步器,提供同步功能。
abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; //加鎖操作,聲明是抽象方法,nofairsync和fairsync中各自實(shí)現(xiàn) abstract void lock(); //非公平獲取,公平性鎖和非公平性鎖都需要這個(gè)方法 final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); //AQS獲取state值 int c = getState(); if (c == 0) { //鎖空閑狀態(tài) //通過cas獲取鎖狀態(tài),修改state狀態(tài) if (compareAndSetState(0, acquires)) { //標(biāo)記當(dāng)前線程為獲取鎖的線程 setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { //鎖非空閑,表明鎖被占中,有一種情況,當(dāng)前線程即為占用鎖的線程 int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); //當(dāng)前線程繼續(xù)持有鎖,僅對(duì)state進(jìn)行加操作 setState(nextc); return true; } return false; } //釋放鎖 sync中的tryRelease是公平性鎖和非公平性鎖的釋放鎖流程都是該方法 protected final boolean tryRelease(int releases) { int c = getState() - releases; //只有持有鎖的線程才能釋放鎖 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) {//鎖才會(huì)被釋放 free = true; setExclusiveOwnerThread(null); } setState(c); return free; } //判斷當(dāng)前線程是否持有鎖 protected final boolean isHeldExclusively() { return getExclusiveOwnerThread() == Thread.currentThread(); } final ConditionObject newCondition() { return new ConditionObject(); } //獲取鎖的持有者線程 final Thread getOwner() { return getState() == 0 ? null : getExclusiveOwnerThread(); } //加鎖的次數(shù) final int getHoldCount() { return isHeldExclusively() ? getState() : 0; } //是否上鎖 true:表示加鎖 final boolean isLocked() { return getState() != 0; } }
該Sync中方法的封裝是調(diào)用AQS中的方法實(shí)現(xiàn)的
公平性鎖和非公平鎖的如何實(shí)現(xiàn)?
公平性鎖:FairLock
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { //同步狀態(tài)為空閑,需要等待隊(duì)列中第一個(gè)等待著執(zhí)行 //什么時(shí)候當(dāng)前線程可以執(zhí)行? 等待隊(duì)列里沒有線程等待或者是有線程等待且等待的第一個(gè)線程就是當(dāng)前線程 if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { //當(dāng)前同步狀態(tài)非空閑,被線程占用且是當(dāng)前線程 int nextc = c + acquires; if (nextc < 0) //有符號(hào)的int類型。最高位為1表示負(fù)數(shù) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } } 類AbstractQueuedSynchronizer#acquire public final void acquire(int arg) { //當(dāng)前同步狀態(tài)非空閑,并且是其他線程持有鎖 返回false if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
公平性鎖獲取鎖流程:
1、如果同步狀態(tài)為空,就可以搶鎖,能夠獲取鎖的前提條件是當(dāng)前等待隊(duì)列為空,或者等待隊(duì)列隊(duì)頭是當(dāng)前線程,即當(dāng)前線程才能夠搶鎖,通過CAS搶鎖(state)搶鎖成功記錄當(dāng)前線程信息到鎖上。
2、如果同步狀態(tài)不為空,即存在線程占用鎖且占用線程是當(dāng)前線程,當(dāng)前線程可成功獲取鎖(state)。
非公平性鎖
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; final void lock() { //執(zhí)行Lock操作,嘗試立即獲取鎖,失敗就退回常規(guī)流程 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1);//立即獲取鎖失敗進(jìn)入到acquire,首先調(diào)用tryAcquire } protected final boolean tryAcquire(int acquires) { //同步狀態(tài)為空閑或者不為空閑但是是當(dāng)前線程持有鎖,返回true表示搶鎖成功 return nonfairTryAcquire(acquires); } } 類AbstractQueuedSynchronizer#acquire public final void acquire(int arg) { //當(dāng)前同步狀態(tài)非空閑,并且是其他線程持有鎖 返回false if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
通過代碼可知:很多方法,trylock,unlock都是在父類sync實(shí)現(xiàn)
非公平性鎖搶鎖流程:
1、直接通過CAS操作搶鎖,如果不成功進(jìn)入常規(guī)搶鎖流程。
2、獲取當(dāng)前鎖的狀態(tài)(state是否為0),如果為0表示空閑,直接通過CAS搶鎖,如果成功,記錄線程信息到鎖上。
3、如果鎖不為空閑且是當(dāng)前線程持有鎖,則可直接獲取鎖(state+1)。
重入鎖的實(shí)現(xiàn): ReentrantLock 都是將具體實(shí)現(xiàn)委托給內(nèi)部類(Sync、NonFairSync、FairSync)。 ReentrantLock 的重入次數(shù)是使用AQS的state屬性,state大于0表示鎖被占用(值表示當(dāng)前線程重入次數(shù)),等于0表示鎖空閑,小于0則表示重入次數(shù)太多導(dǎo)致溢出了。 可重入鎖需要一個(gè)重入計(jì)數(shù)的變量,初始值為0,當(dāng)成功請(qǐng)求鎖加1,釋放鎖時(shí)減1,當(dāng)釋放鎖時(shí)計(jì)數(shù)為0則真正釋放鎖,重入鎖必須持有對(duì)鎖持有者的引用,用以判斷是否可以重入。
(3)Condition
synchronized與wait、notify、notifyAll方法結(jié)合可以實(shí)現(xiàn)等待/通知模式。reentantlock同樣可以實(shí)現(xiàn)等待、通知模式,需要借助于Condition對(duì)象,具有更好的靈活性。
newCondition方法
public Condition newCondition()
Condition中提供的方法如下:
awaitXXX和Object中的wait方法類似,使當(dāng)前線程進(jìn)入休眠等待, signal和Object中的notify方法類似,喚醒一個(gè)處于休眠狀態(tài)的線程 signalAll和Object中的signalAll方法類似,喚醒所有處于休眠狀態(tài)的線程。
生產(chǎn)者和消費(fèi)者
生產(chǎn)者
public class Producer extends Thread { private LinkedList<Integer> cap;//共享倉庫 private Random random = new Random(); private ReentrantLock rlk; private Condition pToc; private Condition cTop; public Producer(LinkedList<Integer> cap, ReentrantLock rlk, Condition pToc, Condition cTop) { this.cap = cap; this.rlk = rlk; this.pToc = pToc; this.cTop = cTop; } @Override public void run() { while (true) { rlk.lock(); try { if (cap.size() == 3) {//緩沖區(qū)滿 生產(chǎn)者進(jìn)行阻塞 System.out.println("緩沖區(qū)滿"); cTop.await(); } //生產(chǎn)產(chǎn)品 int i = random.nextInt(1000); System.out.println("生產(chǎn)者生產(chǎn)了" + i); cap.add(i); //通知消費(fèi)者消費(fèi)產(chǎn)品 pToc.signal(); } catch (InterruptedException e) { throw new RuntimeException(e); } rlk.unlock(); } } }
消費(fèi)者
public class Consumer extends Thread { private LinkedList<Integer> cap;//共享倉庫 private ReentrantLock rlk; private Condition pToc; private Condition cTop; public Consumer(LinkedList<Integer> cap, ReentrantLock rlk, Condition pToc, Condition cTop) { this.cap = cap; this.rlk = rlk; this.pToc = pToc; this.cTop = cTop; } @Override public void run() { while (true) { rlk.lock(); try { if (cap.size() == 0) { //如果緩沖區(qū)為0,消費(fèi)者阻塞 System.out.println("緩沖區(qū)為空"); pToc.await(); } //消費(fèi)者消費(fèi)產(chǎn)品 Integer i = cap.remove(); System.out.println("消費(fèi)者消費(fèi)了" + i); //通知生產(chǎn)者生產(chǎn) cTop.signal(); } catch (InterruptedException e) { throw new RuntimeException(e); } rlk.unlock(); } } }
測(cè)試類
public class Test { public static void main(String[] args) { LinkedList<Integer> cap = new LinkedList<>(); ReentrantLock reentrantLock = new ReentrantLock(); //生產(chǎn)者通知消費(fèi)者 Condition pToc = reentrantLock.newCondition(); //消費(fèi)者通知生產(chǎn)者 Condition cTop = reentrantLock.newCondition(); Producer producer = new Producer(cap,reentrantLock,pToc,cTop); Consumer consumer = new Consumer(cap,reentrantLock,pToc,cTop); producer.start(); consumer.start(); } }
執(zhí)行結(jié)果
- 在調(diào)用Condition中的await或者是signal這些方法中任何的方法時(shí),必須持有鎖(ReentantLock),如果沒有持有此鎖,則拋出IllegalMonitorStateException異常。
- 在調(diào)用await方法時(shí),將釋放掉鎖,并在這些方法返回之前,重新先獲取該鎖,才能執(zhí)行。
- 如果線程在等待中被中斷,則等待將終止,并拋出InterruptedException,清除掉中斷狀態(tài)。
- 等待狀態(tài)的線程按照FIFO順序接收信號(hào)。
- 等待方法返回的線程重新獲取鎖的順序與線程最初獲取鎖的順序是相同的。
循環(huán)打印ABC
ABCThread類
public class ABCThread extends Thread { private String name; private ReentrantLock rtl; private Condition waitc;//等待Condition private Condition sigalc; //通知Condition public ABCThread(String name,ReentrantLock rtl,Condition wc,Condition sc){ this.name = name; this.rtl = rtl; this.waitc = wc; this.sigalc = sc; } @Override public void run() { int num =0; while (true) { rtl.lock(); //等待其他線程通知, try { waitc.await(); } catch (InterruptedException e) { e.printStackTrace(); } //打印當(dāng)前線程信息 System.out.println(name); //通知下一個(gè)線程 sigalc.signal(); ++num; if (num >= 10) break; rtl.unlock(); } } }
測(cè)試
@Test public void test() { ReentrantLock reentrantLock = new ReentrantLock(); //A通知B Condition ab = reentrantLock.newCondition(); //B通知C Condition bc = reentrantLock.newCondition(); //C通知A Condition ca = reentrantLock.newCondition(); new ABCThread("A", reentrantLock, ca, ab).start(); new ABCThread("B", reentrantLock, ab, bc).start(); new ABCThread("C", reentrantLock, bc, ca).start(); //先發(fā)起通知A線程 reentrantLock.lock(); ca.signal(); reentrantLock.unlock(); }
執(zhí)行結(jié)果
到此這篇關(guān)于Java中的ReentrantLock解讀的文章就介紹到這了,更多相關(guān)Java的ReentrantLock內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
新手小白看過來學(xué)JAVA必過IO流File字節(jié)流字符流
這篇文章主要介紹了新手小白學(xué)JAVA到IO流File字節(jié)流字符流的重點(diǎn),對(duì)流不清楚的新手同學(xué)快進(jìn)來學(xué)習(xí)吧,大佬也可以進(jìn)來溫故一下2021-08-08Java多線程環(huán)境下SimpleDateFormat類安全轉(zhuǎn)換
這篇文章主要介紹了Java多線程環(huán)境下SimpleDateFormat類安全轉(zhuǎn)換,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02淺談spring中的default-lazy-init參數(shù)和lazy-init
下面小編就為大家?guī)硪黄獪\談spring中的default-lazy-init參數(shù)和lazy-init。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-04-04Java8使用lambda實(shí)現(xiàn)Java的尾遞歸
這篇文章主要介紹了Java8使用lambda實(shí)現(xiàn)Java的尾遞歸的相關(guān)資料,需要的朋友可以參考下2017-10-10SpringBoot參數(shù)校驗(yàn)的一些實(shí)戰(zhàn)應(yīng)用
這篇文章主要給大家介紹了關(guān)于SpringBoot參數(shù)校驗(yàn)的一些實(shí)戰(zhàn)應(yīng)用,包括使用內(nèi)置的參數(shù)校驗(yàn)注解、嵌套對(duì)象校驗(yàn)、分組校驗(yàn)以及自定義校驗(yàn)注解,通過這些方法,可以有效地提高系統(tǒng)的穩(wěn)定性和安全性,需要的朋友可以參考下2024-11-11Java 數(shù)組交集的實(shí)現(xiàn)代碼
這篇文章主要介紹了Java 數(shù)組交集的實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09淺談Java實(shí)現(xiàn)面向?qū)ο缶幊蘪ava oop
這篇文章主要介紹了淺談Java實(shí)現(xiàn)面向?qū)ο缶幊蘪ava oop,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07