亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java中的ReentrantLock原理解析

 更新時間:2023年11月02日 08:30:49   作者:Heloise_yangyuchang  
這篇文章主要介紹了Java中的ReentrantLock原理解析,ReentrantLock是Java中的一個線程同步工具,它提供了比synchronized更靈活和強(qiáng)大的功能。它是一個可重入的互斥鎖,意味著同一個線程可以多次獲取該鎖,而不會發(fā)生死鎖,需要的朋友可以參考下

一、示例分析

公平鎖

/***
*說明: 該示例使用的是公平策略。 
*/
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class MyThread extends Thread {
    private Lock lock;
    public MyThread(String name, Lock lock) {
        super(name);
        this.lock = lock;
    }
    
    public void run () {
        lock.lock();
        try {
            System.out.println(Thread.currentThread() + " running");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } finally {
            lock.unlock();
        }
    }
}

public class AbstractQueuedSynchonizerDemo {
    public static void main(String[] args) throws InterruptedException {
        Lock lock = new ReentrantLock(true);
        
        MyThread t1 = new MyThread("t1", lock);        
        MyThread t2 = new MyThread("t2", lock);
        MyThread t3 = new MyThread("t3", lock);
        t1.start();
        t2.start();    
        t3.start();
    }
}


//運(yùn)行結(jié)果:

Thread[t1,5,main] running
Thread[t2,5,main] running
Thread[t3,5,main] running
 

二、加鎖lock過程

  1. 獲取當(dāng)前線程
  2. 獲取鎖的狀態(tài)getState()
  3. 判斷鎖的狀態(tài)
  4. 如果鎖是自由狀態(tài)則第五步,如果不是自由狀態(tài)則第七步
  5. 判斷自己是否需要排隊
    • 什么情況下當(dāng)前線程不需要排隊(排隊=入隊+阻塞)
    • 隊列沒有初始 對頭和隊尾等于null的時候是不需要排隊
    • 隊列當(dāng)中只有一個線程的時候是不需要排隊的
  6. 如果不需要排隊則cas加鎖
  7. 判斷是否重入(一般情況下不重入)
  8. 下不重入直接返回false(加鎖失?。?/li>
  9. 加鎖失敗之后會調(diào)用addWaiter,主要是入隊(入隊不等于排隊)入隊完成之后第一個節(jié)點(diǎn)是一個虛擬出來的節(jié)點(diǎn)(thread等于null),即前置節(jié)點(diǎn),而不是我們?nèi)腙牭墓?jié)點(diǎn)
  10. 判斷是否需要自旋
  11. 如果需要自旋則再次獲取鎖,如果失敗則park
  12. 如果不需要自旋則直接park

線程執(zhí)行l(wèi)ock.lock,下圖給出了方法調(diào)用中的主要方法。

在這里插入圖片描述

由上圖可知,最后的結(jié)果是t3線程會被禁止,因為調(diào)用了LockSupport.park。

三、加鎖總結(jié)

  1. AQS框架 第一個線程t1獲取鎖的時候 代價基本為0(cas鎖的狀態(tài),記錄當(dāng)前持有鎖的線程),而且連隊列都為null(隊列都沒有初始化)
  2. 當(dāng)?shù)谝粋€線程釋放鎖之后第二個線程t2來加鎖(t1 和t2是沒有競爭執(zhí)行),代價基本為0,(cas鎖的狀態(tài),記錄當(dāng)前持有鎖的線程),而且連隊列都為null(隊列都沒有初始化),因為是重復(fù)上面步驟6
  3. t1沒有釋放鎖這個t2來加鎖,鎖的情況如下
    • t2會加鎖失敗,則返回false,然后調(diào)用addWaiter方法區(qū)初始化隊列,然后自己入隊
    • 會把t2封裝成為一個node,調(diào)用ENQ方法讓當(dāng)前node入隊
    • 入隊成功之后調(diào)用acquireQueued final Node p = node.predecessor();獲取上一個節(jié)點(diǎn)
    • 判斷上一個節(jié)點(diǎn)是否頭節(jié)點(diǎn)
    • 如果是頭結(jié)點(diǎn)則再次獲取鎖(一次自旋)
    • 如果獲取到了鎖,表示t1釋放了鎖(AQS框架當(dāng)中第一個node永遠(yuǎn)可以理解為是當(dāng)前持有鎖的線程)
    • 如果t1沒有釋放鎖,t2自旋一次之后還是沒有獲取到鎖則park 排隊
    • t3來加鎖,t1沒有釋放鎖,t2這個時候已經(jīng)排隊(和t2的區(qū)別在于t3入隊之后不會自旋,直接排 隊)。
  4. t3 因為t1沒有釋放鎖,所以t3肯定拿不到鎖,肯定會調(diào)用addWaiter–enq方法入隊。

四、解鎖unlock

第一種情況:

  1. 只有一個t1上鎖了,當(dāng)調(diào)用unlock解鎖的時候,sync.release(1);
  2. 把鎖的狀態(tài)改為自由狀態(tài)
  3. boolean free = false; 只是標(biāo)識一下目前還沒有釋放鎖成功,因為你僅僅把鎖改成了自由狀態(tài),線程沒有釋放(而且還有可能是重入),所以這個變量是一個過渡變量。
  4. setExclusiveOwnerThread(null) 把持有鎖的線程改為null 鎖徹底釋放了
  5. 以上是釋放鎖,接下來可能需要喚醒隊列當(dāng)中的阻塞線程去獲取鎖(因為有可能不需要喚醒)
  6. Node h = head;拿到隊頭if (h != null && h.waitStatus != 0)判斷是否有對頭,是否有線程在排隊
  7. 由于當(dāng)前這種情況肯定沒有人排隊則不需要喚醒,則return true 標(biāo)識解鎖成功

第二種情況:

就是隊列當(dāng)中有線程排隊,比如t2

  1. 調(diào)用tryRelease方法釋放
  2. if (h != null && h.waitStatus != 0) 判斷是否需要喚醒下一個,當(dāng)前這種情況肯定需要喚醒一個
  3. Node h = head;把頭結(jié)點(diǎn)傳給了unparkSuccessor,unparkSuccessor(h);
  4. 得到隊列當(dāng)中第一個排隊的線程, 也就是t2所標(biāo)識的node對象LockSupport.unpark(s.thread); 喚醒t2線程
  5. 由于t2在lock方法中被阻塞那么喚醒則也是從lock方法中被喚醒往下執(zhí)行
 private final boolean parkAndCheckInterrupt() {
  //簡單的喚醒t2
   LockSupport.park(this); 
   //這里為什么需要調(diào)用一下Thread.interrupted() 
   return Thread.interrupted();

 }

Thread.interrupted()這個方法主要干嘛?清除打斷標(biāo)記(復(fù)位)

五、內(nèi)部類

在這里插入圖片描述

Sync類存在如下方法和作用如下。

在這里插入圖片描述

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)) // 比較并設(shè)置狀態(tài)成功,狀態(tài)0表示鎖沒有被占用
            // 把當(dāng)前線程設(shè)置獨(dú)占了鎖
            setExclusiveOwnerThread(Thread.currentThread());
        else // 鎖已經(jīng)被占用,或者set失敗
            // 以獨(dú)占模式獲取對象,忽略中斷
            acquire(1); 
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

從lock方法的源碼可知,每一次都嘗試獲取鎖,而并不會按照公平等待的原則進(jìn)行等待,讓等待時間最久的線程獲得鎖。

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

// 公平鎖
static final class FairSync extends Sync {
    // 版本序列化
    private static final long serialVersionUID = -3000897897090466540L;

    final void lock() {
        // 以獨(dú)占模式獲取對象,忽略中斷
        acquire(1);
    }

    /**
        * Fair version of tryAcquire.  Don't grant access unless
        * recursive call or no waiters or is first.
        */
    // 嘗試公平獲取鎖
    protected final boolean tryAcquire(int acquires) {
        // 獲取當(dāng)前線程
        final Thread current = Thread.currentThread();
        // 獲取狀態(tài)
        int c = getState();
        if (c == 0) { // 狀態(tài)為0
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) { // 不存在已經(jīng)等待更久的線程并且比較并且設(shè)置狀態(tài)成功
                // 設(shè)置當(dāng)前線程獨(dú)占
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) { // 狀態(tài)不為0,即資源已經(jīng)被線程占據(jù)
            // 下一個狀態(tài)
            int nextc = c + acquires;
            if (nextc < 0) // 超過了int的表示范圍
                throw new Error("Maximum lock count exceeded");
            // 設(shè)置狀態(tài)
            setState(nextc);
            return true;
        }
        return false;
    }
}
  

跟蹤lock方法的源碼可知,當(dāng)資源空閑時,它總是會先判斷sync隊列(AbstractQueuedSynchronizer中的數(shù)據(jù)結(jié)構(gòu))是否有等待時間更長的線程,如果存在,則將該線程加入到等待隊列的尾部,實(shí)現(xiàn)了公平獲取原則。其中,F(xiàn)airSync類的lock的方法調(diào)用如下,只給出了主要的方法。

在這里插入圖片描述

可以看出只要資源被其他線程占用,該線程就會添加到sync queue中的尾部,而不會先嘗試獲取資源。這也是和Nonfair最大的區(qū)別,Nonfair每一次都會嘗試去獲取資源,如果此時該資源恰好被釋放,則會被當(dāng)前線程獲取,這就造成了不公平的現(xiàn)象,當(dāng)獲取不成功,再加入隊列尾部。

六、類的構(gòu)造函數(shù)

ReentrantLock()型構(gòu)造函數(shù);默認(rèn)是采用的非公平策略獲取鎖

public ReentrantLock() {
    // 默認(rèn)非公平策略
    sync = new NonfairSync();
}

ReentrantLock(boolean)型構(gòu)造函數(shù)

可以傳遞參數(shù)確定采用公平策略或者是非公平策略,參數(shù)為true表示公平策略,否則,采用非公平策略:

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

核心函數(shù)分析

通過分析ReentrantLock的源碼,可知對其操作都轉(zhuǎn)化為對Sync對象的操作,由于Sync繼承了AQS,所以基本上都可以轉(zhuǎn)化為對AQS的操作。如將ReentrantLock的lock函數(shù)轉(zhuǎn)化為對Sync的lock函數(shù)的調(diào)用,而具體會根據(jù)采用的策略(如公平策略或者非公平策略)的不同而調(diào)用到Sync的不同子類。 所以可知,在ReentrantLock的背后,是AQS對其服務(wù)提供了支持。

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

相關(guān)文章

  • 解決SpringCloud Feign傳對象參數(shù)調(diào)用失敗的問題

    解決SpringCloud Feign傳對象參數(shù)調(diào)用失敗的問題

    這篇文章主要介紹了解決SpringCloud Feign傳對象參數(shù)調(diào)用失敗的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Springboot自定義全局異常問題

    Springboot自定義全局異常問題

    這篇文章主要介紹了Springboot自定義全局異常問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • SpringBoot特點(diǎn)之依賴管理和自動裝配(實(shí)例代碼)

    SpringBoot特點(diǎn)之依賴管理和自動裝配(實(shí)例代碼)

    在使用SpringBoot的時候,會自動將Bean裝配到IoC容器中,操作也很簡單,今天小編給大家介紹下SpringBoot特點(diǎn)之依賴管理和自動裝配的知識,感興趣的朋友一起看看吧
    2022-03-03
  • Spring Boot整合RabbitMQ實(shí)例(Topic模式)

    Spring Boot整合RabbitMQ實(shí)例(Topic模式)

    Topic Exchange 轉(zhuǎn)發(fā)消息主要是根據(jù)通配符。接下來通過本文給大家分享Spring Boot整合RabbitMQ實(shí)例(Topic模式),需要的朋友參考下吧
    2017-04-04
  • Springboot  jar包 idea 遠(yuǎn)程調(diào)試的操作過程

    Springboot  jar包 idea 遠(yuǎn)程調(diào)試的操作過程

    文章介紹了如何在IntelliJ IDEA中遠(yuǎn)程調(diào)試Spring Boot項目的Jar包,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • Activiti7通過代碼動態(tài)生成工作流實(shí)現(xiàn)詳解

    Activiti7通過代碼動態(tài)生成工作流實(shí)現(xiàn)詳解

    這篇文章主要為大家介紹了Activiti7通過代碼動態(tài)生成工作流實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • JAVA面試題之Forward與Redirect的區(qū)別詳解

    JAVA面試題之Forward與Redirect的區(qū)別詳解

    這篇文章主要給大家介紹了在JAVA面試中可能遇到會遇到的一道題,就是java中Forward與Redirect兩者之前的區(qū)別,文中介紹的非常詳細(xì),對大家具有一定參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。
    2017-05-05
  • Java中的Native方法

    Java中的Native方法

    這篇文章主要介紹了Java中的Native方法,在本文中,我們將看到j(luò)ava中本機(jī)native方法的介紹。我們將看到它的基本語法及其工作原理。將有java代碼示例展示native本機(jī)方法的使用,下面來看看文章的具體介紹
    2021-12-12
  • 詳解Spring中@Valid和@Validated注解用法

    詳解Spring中@Valid和@Validated注解用法

    本文將以新增一個員工為功能切入點(diǎn),以常規(guī)寫法為背景,慢慢烘托出?@Valid?和?@Validated?注解用法詳解,文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-07-07
  • spring boot密碼加密配置與實(shí)例詳解

    spring boot密碼加密配置與實(shí)例詳解

    BCrypt是一種專為密碼哈希設(shè)計的算法,它被廣泛認(rèn)為是安全的選擇之一,這篇文章主要介紹了spring boot密碼加密配置與實(shí)例詳解,需要的朋友可以參考下
    2024-12-12

最新評論