欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java可重入鎖ReentrantLock詳解

 更新時(shí)間:2023年09月27日 10:49:05   作者:賢子磊  
這篇文章主要介紹了Java可重入鎖ReentrantLock詳解,ReentrantLock是一個(gè)可重入且獨(dú)占式的鎖,是一種遞歸無阻塞的同步機(jī)制,它支持重復(fù)進(jìn)入鎖,即該鎖能夠支持一個(gè)線程對資源的重復(fù)加鎖,除此之外,該鎖的還支持獲取鎖時(shí)的公平和非公平性選擇,需要的朋友可以參考下

Lock接口

在講 ReentrantLock 前,我們先熟悉一下 Lock 接口。在 Lock 接口出現(xiàn)之前, Java 程序主要是靠 synchronized 關(guān)鍵字實(shí)現(xiàn)鎖功能的,而JDK1.5之后,并發(fā)包中增加了 Lock 接口,它提供了與 synchronized 一樣的鎖功能。雖然它失去了像synchronize關(guān)鍵字隱式加鎖解鎖的便捷性,但是卻擁有了鎖獲取和釋放的可操作性,可中斷的獲取鎖以及超時(shí)獲取鎖等多種 synchronized 關(guān)鍵字所不具備的同步特性。 Lock 接口的定義如下

public interface Lock {
    //獲取鎖,若當(dāng)前鎖被其他線程獲取,則此線程會(huì)阻塞等待lock被釋放
    //使用lock方法需要顯式地去釋放鎖,即使發(fā)生異常時(shí)也不會(huì)自動(dòng)釋放鎖。
    void lock();
    //作用同lock方法,并且在獲取鎖的過程中可以響應(yīng)中斷
    void lockInterruptibly() throws InterruptedException;
    //嘗試獲取鎖,獲取成功返回true,失敗則返回false
    //即該方法不會(huì)導(dǎo)致線程阻塞,無論如何都會(huì)返回
    boolean tryLock();
    //作用同tryLock方法,如果獲取到鎖直接返回true
    //新增的特性是如果獲取不到鎖,會(huì)等待一段時(shí)間,且在等待的過程可以響應(yīng)中斷,一旦超過等待時(shí)間仍獲取不到鎖,就返回false
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    //釋放鎖
    void unlock();
    //返回一個(gè)綁定該lock的Condition對象,這個(gè)后續(xù)在Condition專題會(huì)詳細(xì)講解,本文先不贅述
    Condition newCondition();
}

ReentrantLock 完全實(shí)現(xiàn)了Lock接口,也是JDK中唯一實(shí)現(xiàn)Lock接口的類(其余都是一些內(nèi)部類)。Lock接口的基本模板使用如下所示

//創(chuàng)建lock實(shí)例
Lock lock = new ReentrantLock();
try {
    //加鎖
    lock.lock();
    // do something...
} finally {
    //解鎖
    lock.unlock();
}

什么是ReentrantLock

ReentrantLock是一個(gè)可重入且獨(dú)占式的鎖,是一種遞歸無阻塞的同步機(jī)制。

它支持重復(fù)進(jìn)入鎖,即該鎖能夠支持一個(gè)線程對資源的重復(fù)加鎖。

除此之外,該鎖的還支持獲取鎖時(shí)的公平和非公平性選擇。

可重入概念

重進(jìn)入是指任意線程在獲取到鎖之后能夠再次獲取該鎖而不會(huì)被鎖阻塞,該特性的首先需要解決以下兩個(gè)問題:

  • 線程再次獲取鎖:所需要去識別獲取鎖的線程是否為當(dāng)前占據(jù)鎖的線程,如果是,則再次獲取成功;
  • 鎖的最終釋放:線程重復(fù)n次獲取了鎖,隨后在第n次釋放該鎖后,其它線程能夠獲取到該鎖。鎖的最終釋放要求鎖對于獲取進(jìn)行計(jì)數(shù)自增,計(jì)數(shù)表示當(dāng)前線程被重復(fù)獲取的次數(shù),而被釋放時(shí),計(jì)數(shù)自減,當(dāng)計(jì)數(shù)為0時(shí)表示鎖已經(jīng)成功釋放。

ReentrantLock和Synchronized對比

共同點(diǎn)

  • 都可以協(xié)調(diào)多線程對共享對象、變量的訪問
  • 都是可重入的,同一個(gè)線程可以多次獲得同一個(gè)鎖
  • 都是阻塞式地同步,即一個(gè)線程獲取了鎖,其他訪問該鎖的線程必須阻塞在同步塊外等待
  • 性能方面:Synchronized在JDK1.6的一波優(yōu)化后性能與lock差別不大

區(qū)別

  • ReentrantLock需要自己手動(dòng)地獲取和釋放鎖,而synchronized關(guān)鍵字可以隱式獲得和釋放,無需用戶操心。
  • ReentrantLock具有響應(yīng)中斷,超時(shí)獲取,公平非公平鎖和利用Condition綁定多個(gè)條件等特性,而synchronized不具備這些特性
  • ReentrantLock是API級別的,而synchronized是JVM級別的
  • ReentrantLock底層實(shí)現(xiàn)采用的是同步非阻塞,樂觀并發(fā)(CAS)策略。而synchronized是同步阻塞,悲觀并發(fā)。

類的繼承關(guān)系

類總覽:

ReentrantLock實(shí)現(xiàn)類Lock和Serializable接口。

public class ReentrantLock implements Lock, java.io.Serializable

類的內(nèi)部類

ReentrantLock 類內(nèi)部總共存在Sync、NonfairSync、FairSync三個(gè)類,其中 NonfairSync 與 FairSync 類繼承自 Sync 類, Sync 類繼承自 AbstractQueuedSynchronizer 抽象類。

Sync

Sync類繼承自AQS,它有兩個(gè)子類,分別實(shí)現(xiàn)公平鎖和非公平鎖

abstract static class Sync extends AbstractQueuedSynchronizer {
    //序列化版本號
    private static final long serialVersionUID = -5179523762034025860L;
    //獲取鎖,需要子類自己實(shí)現(xiàn)
    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()) {
            int nextc = c + acquires;
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
    //釋放鎖
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
    //判斷資源釋放被當(dāng)前線程占用
    protected final boolean isHeldExclusively() {
        return getExclusiveOwnerThread() == Thread.currentThread();
    }
    //創(chuàng)建一個(gè)新條件
    final ConditionObject newCondition() {
        return new ConditionObject();
    }
    // 返回占有資源的線程
    final Thread getOwner() {
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }
    //返回狀態(tài)
    final int getHoldCount() {
        return isHeldExclusively() ? getState() : 0;
    }
    //判斷資源是否被占用
    final boolean isLocked() {
        return getState() != 0;
    }
    //自定義反序列化
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        setState(0); // reset to unlocked state
    }
}

NonfairSync

NonfairSync類繼承了Sync類,表示采用非公平策略獲取鎖,其實(shí)現(xiàn)了Sync類中抽象的lock方法

static final class NonfairSync extends Sync {
    //序列化版本號
    private static final long serialVersionUID = 7316153563782823691L;
    //獲取鎖
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }
    //嘗試獲取鎖
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

方法細(xì)節(jié)后面詳細(xì)說明

FairSync

FairSync類也繼承了Sync類,表示采用公平策略獲取鎖,其也實(shí)現(xiàn)了Sync類中的抽象lock方法

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) {
            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;
        }
        return false;
    }
}

方法細(xì)節(jié)后面詳細(xì)說明

類的屬性

//序列化版本號
private static final long serialVersionUID = 7373984872572414699L;
//同步器,繼承自AQS,提供各種核心操作
private final Sync sync;

sync屬性非常重要,大部分的鎖操作直接轉(zhuǎn)化為該屬性的方法調(diào)用。

鎖類型

ReentrantLock 分為公平鎖和非公平鎖,分別有它的兩個(gè)內(nèi)部類 FairSync 和 NonfairSync 實(shí)現(xiàn)。

公平鎖

加鎖前檢查是否有排隊(duì)等待的線程,優(yōu)先排隊(duì)等待的線程,先來先得,即遵循FIFO原則。

非公平鎖

加鎖時(shí)不考慮排隊(duì)等待問題,直接嘗試獲取鎖,獲取不到自動(dòng)到隊(duì)尾等待

是否公平可以通過構(gòu)造方法指定。

//無參構(gòu)造函數(shù),默認(rèn)為非公平鎖
public ReentrantLock() {
    sync = new NonfairSync();
}
//有參構(gòu)造函數(shù),傳遞boolean類型參數(shù)fair
//fair=false:非公平鎖
//fair=true:公平鎖
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

注:公平鎖為了保證時(shí)間上的絕對順序,需要頻繁的上下文切換,而非公平鎖會(huì)降低一定的上下文切換,降低性能開銷。因此, ReentrantLock 默認(rèn)選擇的是非公平鎖,則是為了減少一部分上下文切換,保證了系統(tǒng)更大的吞吐量。

獲取鎖

在講解 ReentrantLock 的源碼前,需要大家對AQS的源碼熟悉

公平鎖的獲取

我們知道,lock的獲取鎖一般使用如下方法

lock.lock();

那我們直接沿著該方法往下看,先看lock方法的實(shí)現(xiàn)

public void lock() {
    sync.lock();
}

調(diào)用屬性sync的lock方法,繼續(xù)深入(此時(shí)是公平鎖,所以調(diào)用的是 FairSync 對象中的 lock 方法)

final void lock() {
    acquire(1);
}

繼續(xù)查看acquire方法

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

看到這里想必大家已經(jīng)很熟悉了,這就是AQS的acquire方法,這里就不再贅述,大家可以參考之前的AQS源碼分析文章。但是我們需要分析一下tryAcquire方法,因?yàn)樵摲椒ㄓ蠷eentrantLock中FairSync類自己實(shí)現(xiàn)的,源碼如下

protected final boolean tryAcquire(int acquires) {
    //獲取當(dāng)前線程
    final Thread current = Thread.currentThread();
    //獲取同步狀態(tài)
    int c = getState();
    //如果同步狀態(tài)為0表示當(dāng)前資源空閑
    if (c == 0) {
        //在CAS獲取鎖前需要調(diào)用方法hasQueuedPredecessors來判斷隊(duì)列中是否存在其他正在排隊(duì)的節(jié)點(diǎn),如果存在返回true,不僅如此if塊,否則返回false,進(jìn)行CAS操作
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            //設(shè)置當(dāng)前資源的持有線程為當(dāng)前線程
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //如果c不等于0,且當(dāng)前持有鎖線程不等于當(dāng)前線程,直接返回false,加鎖失敗
    //如果c不等于0,且當(dāng)前持有鎖的線程等于當(dāng)前線程,表示這是一次重入,就會(huì)把狀態(tài)+1,結(jié)束后返回true,即重入鎖返回true
    //這塊代碼側(cè)面說明Reentrantlock是可以重入的
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

這里需要詳細(xì)說明一下 hasQueuedPredecessors 方法(該方法的代碼也是整個(gè) Reentrantlock 代碼中的精華部分之一),需要注意一下,該方法如果返回false表示當(dāng)前線程可以直接獲取鎖,否則獲取鎖失敗。代碼如下

public final boolean hasQueuedPredecessors() {
	//保存尾結(jié)點(diǎn)
    Node t = tail; 
    //保存頭結(jié)點(diǎn)
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

重點(diǎn)看一下return后面的判斷

h != t &&((s = h.next) == null || s.thread != Thread.currentThread())
  • 先看條件h != t
    • 如果h=t,大概分為以下兩種情況
      • 隊(duì)列未初始化,此時(shí)h=t=null,說明肯定沒有排隊(duì)等待的線程,可以直接獲取鎖,所以返回false
      • 隊(duì)列已經(jīng)初始化 ,但是不存在排隊(duì)等待的線程。我們知道隊(duì)列在初始化的時(shí)候會(huì)新建個(gè)節(jié)點(diǎn)(thread=null)作為頭結(jié)點(diǎn),往后需要排隊(duì)的線程鏈接到這之后。因此當(dāng)隊(duì)列只有一個(gè)節(jié)點(diǎn)時(shí),滿足h=t,此時(shí)返回false,可以直接獲取鎖
    • 如果h!=t,說明隊(duì)列節(jié)點(diǎn)個(gè)數(shù)大于1,即肯定存在排隊(duì)等待的線程,此時(shí)h!=t返回true,需要繼續(xù)進(jìn)行下一步判斷
  • 再看條件(s = h.next) == null || s.thread != Thread.currentThread()
    • 先看(s = h.next) == null:通過上面的分析我們知道隊(duì)列的節(jié)點(diǎn)個(gè)數(shù)大于1,所以s(即頭結(jié)點(diǎn)的下一節(jié)點(diǎn))肯定不為空,返回false;
    • 再看s.thread != Thread.currentThread():能夠執(zhí)行到此處,整個(gè)return后面的判斷語句的結(jié)果直接取決于當(dāng)前判斷。這里判斷s節(jié)點(diǎn)對應(yīng)的線程是否是當(dāng)前線程,如果是說明此時(shí)排隊(duì)的線程就是當(dāng)前線程,即可以獲取鎖,返回false。如果不是則表示當(dāng)前還沒有輪到我(當(dāng)前線程)獲取鎖,返回true。

總結(jié)一下 hasQueuedPredecessors 方法:該方法的作用就是查看是否有排隊(duì)的線程,如果沒有直接返回false,代表當(dāng)前線程可以獲取鎖,否則再查看第一個(gè)排隊(duì)的線程是否是自己,如果是則返回false,可以獲取鎖,否則就獲取鎖失敗,進(jìn)入后續(xù)的入隊(duì)操作。

非公平鎖的獲取

非公平鎖的獲取,同樣我們來到 NonfairSync 對象中的 lock 方法

final void lock() {
    //直接嘗試獲取鎖
    if (compareAndSetState(0, 1))
        //獲取鎖成功后
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

非公平鎖的lock方法一開始就嘗試獲取CAS獲取鎖,如果CAS失敗再調(diào)用方法acquire去進(jìn)行相應(yīng)的排隊(duì)操作。

釋放鎖

公平鎖和非公平鎖的釋放流程都是一樣的

public void unlock() {
    sync.release(1);
}

調(diào)用AQS中的release方法

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

這里我們詳細(xì)講解一下tryRelease方法,代碼如下

protected final boolean tryRelease(int releases) {
    //計(jì)算釋放后state值
    int c = getState() - releases;
    //如果獲取鎖的線程不是當(dāng)前線程,直接拋異常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //鎖被重入的次數(shù)為0,表示鎖被釋放,清空獨(dú)占線程
    if (c == 0) {
        free = true;
        //清空獨(dú)占線程
        setExclusiveOwnerThread(null);
    }
    //設(shè)置state值
    setState(c);
    //返回鎖是否被釋放
    return free;
}

方法詳解見注釋,這里需要注意一下,調(diào)用unlock方法前需要保證鎖的占有者必須是當(dāng)前方法的調(diào)用者,否則或拋出IllegalMonitorStateException異常。

到此這篇關(guān)于Java可重入鎖ReentrantLock詳解的文章就介紹到這了,更多相關(guān)ReentrantLock詳解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 搭建maven私有倉庫的方法實(shí)現(xiàn)

    搭建maven私有倉庫的方法實(shí)現(xiàn)

    Maven是一個(gè)流行的Java項(xiàng)目管理工具,它可以幫助我們管理項(xiàng)目的構(gòu)建、報(bào)告和文檔,本文主要介紹了搭建maven私有倉庫的方法實(shí)現(xiàn),感興趣的可以了解一下
    2023-05-05
  • 探索Java分布式限流技術(shù)

    探索Java分布式限流技術(shù)

    探索Java分布式限流技術(shù),讓你的系統(tǒng)遠(yuǎn)離流量過載的煩惱,本指南將帶你了解如何使用Java實(shí)現(xiàn)高效的限流策略,幫助你輕松應(yīng)對高并發(fā)場景,讓我們一起開啟這段精彩的技術(shù)之旅,打造更加穩(wěn)定可靠的系統(tǒng),需要的朋友可以參考下
    2024-03-03
  • JDK動(dòng)態(tài)代理詳細(xì)解析

    JDK動(dòng)態(tài)代理詳細(xì)解析

    這篇文章主要介紹了JDK動(dòng)態(tài)代理詳細(xì)解析,在Java的動(dòng)態(tài)代理機(jī)制中,有兩個(gè)重要的類和接口,一個(gè)是InvoInvocationHandler(接口)、Proxy(類),這一個(gè)類和接口是我們動(dòng)態(tài)代理所必須用到的,需要的朋友可以參考下
    2023-11-11
  • 從0開始教你開發(fā)一個(gè)springboot應(yīng)用

    從0開始教你開發(fā)一個(gè)springboot應(yīng)用

    這篇文章主要為大家介紹了從0開始開發(fā)一個(gè)springboot應(yīng)用教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • Java 邏輯運(yùn)算符中&&與&,||與|的區(qū)別

    Java 邏輯運(yùn)算符中&&與&,||與|的區(qū)別

    這篇文章主要介紹了Java中&&與&,||與|的區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • SpringBoot接收數(shù)組參數(shù)和集合參數(shù)方式

    SpringBoot接收數(shù)組參數(shù)和集合參數(shù)方式

    這篇文章主要介紹了SpringBoot接收數(shù)組參數(shù)和集合參數(shù)方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • spring cloud將spring boot服務(wù)注冊到Eureka Server上的方法

    spring cloud將spring boot服務(wù)注冊到Eureka Server上的方法

    本篇文章主要介紹了spring cloud將spring boot服務(wù)注冊到Eureka Server上的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • Spring中@RefreshScope注解的處理方法詳解

    Spring中@RefreshScope注解的處理方法詳解

    這篇文章主要介紹了Spring中@RefreshScope注解的處理方法詳解,spring啟動(dòng)時(shí)會(huì)調(diào)用ClassPathBeanDefinitionScanner.java類中的doScan()對包路徑下的所有class進(jìn)行掃描,獲取bean的定義,同時(shí)對bean的@RefreshScope(@Scope的父類)進(jìn)行處理,需要的朋友可以參考下
    2023-10-10
  • 不寫mybatis的@Param有的報(bào)錯(cuò)有的卻不報(bào)錯(cuò)問題分析

    不寫mybatis的@Param有的報(bào)錯(cuò)有的卻不報(bào)錯(cuò)問題分析

    這篇文章主要為大家介紹了不寫mybatis的@Param有的報(bào)錯(cuò)有的卻不報(bào)錯(cuò)問題分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Java數(shù)組模擬優(yōu)先級隊(duì)列數(shù)據(jù)結(jié)構(gòu)的實(shí)例

    Java數(shù)組模擬優(yōu)先級隊(duì)列數(shù)據(jù)結(jié)構(gòu)的實(shí)例

    這篇文章主要介紹了Java數(shù)組模擬優(yōu)先級隊(duì)列數(shù)據(jù)結(jié)構(gòu)的實(shí)例,優(yōu)先級隊(duì)列中的元素會(huì)被設(shè)置優(yōu)先權(quán),本文的例子借助了Java中的TreeSet和TreeMap,需要的朋友可以參考下
    2016-04-04

最新評論