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

深度解析Java中的ReentrantLock原理

 更新時(shí)間:2023年07月20日 10:03:09   作者:我是小趴菜  
這篇文章主要介紹了關(guān)于ReentrantLock的原理解析,文章通過代碼示例介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下

在高并發(fā)編程中,AbstractQueuedSynchronizer(簡稱AQS)抽象的隊(duì)列同步器是我們必須掌握的,AQS底層提供了二種鎖模式

  • 獨(dú)占鎖:ReentrantLock就是基于獨(dú)占鎖模式實(shí)現(xiàn)的
  • 共享鎖:CountDownLatch,ReadWriteLock,Semplere都是基于共享鎖模式實(shí)現(xiàn)的

接下來我們通過ReentrantLock底層實(shí)現(xiàn)原理來了解AQS獨(dú)占鎖模式的實(shí)現(xiàn)原理

ReentrantLock用法

我們創(chuàng)建三個(gè)線程去獲取資源,然后執(zhí)行業(yè)務(wù)邏輯

ReentrantLock lock = new ReentrantLock();
new Thread(() -> {
    try{
        lock.lock();
        TimeUnit.SECONDS.sleep(10);
    }catch (Exception e){
    }finally {
        lock.unlock();
    }
},"t1").start();
new Thread(() -> {
    try{
        lock.lock();
        TimeUnit.SECONDS.sleep(10);
    }catch (Exception e){
    }finally {
        lock.unlock();
    }
},"t2").start();
new Thread(() -> {
    try{
        lock.lock();
        TimeUnit.SECONDS.sleep(10);
    }catch (Exception e){
    }finally {
        lock.unlock();
    }
},"t3").start();

首先分析一下獲取鎖的原理,也就是 lock.lock()方法

public void lock() {
    //繼續(xù)進(jìn)入這個(gè)方法
    sync.lock();
}

進(jìn)入這個(gè)方法的時(shí)候有兩個(gè)實(shí)現(xiàn)類,一個(gè)是公平鎖FairSync,還有一個(gè)就是非公平鎖NonfairSync,由于非公平鎖比公平鎖復(fù)雜,所以我們先分析非公平鎖的原理

final void lock() {
    //這里就會(huì)嘗試去獲取鎖,如果成功獲取到了鎖,此時(shí)state的值就會(huì)從0變成1
    if (compareAndSetState(0, 1))
        //就把exclusiveOwnerThread的值設(shè)置成當(dāng)前線程
        //exclusiveOwnerThread是AQS里面的一個(gè)變量,也就是線程獲取到了鎖之后,就會(huì)把這個(gè)值設(shè)置成當(dāng)前線程
        setExclusiveOwnerThread(Thread.currentThread());
    else
        // 如果沒有獲取到鎖,就執(zhí)行下面這個(gè)方法
        acquire(1);
}

進(jìn)入這個(gè)方法的時(shí)候有兩個(gè)實(shí)現(xiàn)類,一個(gè)是公平鎖FairSync,還有一個(gè)就是非公平鎖NonfairSync,由于非公平鎖比公平鎖復(fù)雜,所以我們先分析非公平鎖的原理

final void lock() {
    //這里就會(huì)嘗試去獲取鎖,如果成功獲取到了鎖,此時(shí)state的值就會(huì)從0變成1
    if (compareAndSetState(0, 1))
        //就把exclusiveOwnerThread的值設(shè)置成當(dāng)前線程
        //exclusiveOwnerThread是AQS里面的一個(gè)變量,也就是線程獲取到了鎖之后,就會(huì)把這個(gè)值設(shè)置成當(dāng)前線程
        setExclusiveOwnerThread(Thread.currentThread());
    else
        // 如果沒有獲取到鎖,就執(zhí)行下面這個(gè)方法
        acquire(1);
}

第一個(gè)線程進(jìn)來之后,由于state等于0,那么就可以獲取到鎖,于是就會(huì)把state的值設(shè)置成1,然后exclusiveOwnerThread值設(shè)置成自己的線程

這時(shí)候第二個(gè)線程進(jìn)來,發(fā)現(xiàn)state的值已經(jīng)是1了,所以就會(huì)進(jìn)入到acquire(1);這個(gè)方法了

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

這里一共有三個(gè)方法,我們先分析tryAcquire(arg);這里的參數(shù)值等于1

//還是進(jìn)入到非公平鎖的實(shí)現(xiàn)類NonfairSync
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
    //拿到當(dāng)前線程
    final Thread current = Thread.currentThread();
    //獲取到state的值,由于之前已經(jīng)有線程獲取到鎖了,所以這個(gè)值現(xiàn)在等于1,也就不會(huì)進(jìn)入帶if分支
    int c = getState();
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //判斷此時(shí)獲取到鎖的線程是不是自己,這里就是可重入鎖的的實(shí)現(xiàn)原理了,但是我們是三個(gè)不同的線程
    //所以也不會(huì)進(jìn)入到這里
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    //所以最后返回false
    return false;
}

所以tryAcquire(arg)方法做了以下幾個(gè)步驟

  • 1:繼續(xù)嘗試獲取鎖,如果獲取到鎖了就直接返回
  • 2:如果沒有獲取到鎖,就判斷此時(shí)獲取到鎖的線程是不是自己,如果是,就可以獲取到資源繼續(xù)執(zhí)行,也就是可重入鎖的原理
  • 3:如果以上二個(gè)步驟都不符合,就直接返回false

第一個(gè)tryAcquire(arg)方法最后返回false,但是這里是取返,所以是為true,所以就會(huì)進(jìn)入acquireQueued()方法,但是在這里還有個(gè)addWaiter(Node.EXCLUSIVE), arg)方法,所以我們先分析這個(gè)方法

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //Node.EXCLUSIVE表示是獨(dú)占模式
        selfInterrupt();
}
private Node addWaiter(Node mode) {
    //將當(dāng)前線程封裝成一個(gè)Node節(jié)點(diǎn),并設(shè)置成獨(dú)占模式,
    //注意:一個(gè)新的Node節(jié)點(diǎn)的waitStatus的值等于0
    Node node = new Node(Thread.currentThread(), mode);
    //在第一次進(jìn)來的時(shí)候,tail和head都是null,所以不會(huì)進(jìn)入到if分支里面去
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    //由于head和tail都為null,所以會(huì)進(jìn)入初始化鏈表的方法
    //初始化鏈表
    enq(node);
    return node;
}
private Node enq(final Node node) {
    for (;;) {  //注意:這里是死循環(huán)
        Node t = tail;
        //第一次進(jìn)來,因?yàn)閠ail=null,所以會(huì)進(jìn)入到if里面去
        if (t == null) { // Must initialize
            //這里新創(chuàng)建一個(gè)空的Node節(jié)點(diǎn)
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}
  • 第一次進(jìn)來:因?yàn)榈谝淮芜M(jìn)來的時(shí)候tail=null,所以會(huì)進(jìn)入到if中去,然后創(chuàng)建一個(gè)新的空的節(jié)點(diǎn),然后將頭節(jié)點(diǎn)和尾節(jié)點(diǎn)都指向這個(gè)節(jié)點(diǎn)

  • 然后進(jìn)入第二次循環(huán):這時(shí)候tail已經(jīng)不為空了,所以會(huì)進(jìn)入到else分支里面去,所以的操作就是將當(dāng)前線程封裝成的Node設(shè)置尾巴節(jié)點(diǎn),然后設(shè)置前置節(jié)點(diǎn)和后置節(jié)點(diǎn)的關(guān)系

這時(shí)候第三個(gè)線程進(jìn)來,然后假設(shè)也沒有獲取到鎖,那么也會(huì)進(jìn)入到addWaiter()方法中

private Node addWaiter(Node mode) {
    //將當(dāng)前線程封裝成一個(gè)Node節(jié)點(diǎn),并設(shè)置成獨(dú)占模式,
    //注意:一個(gè)新的Node節(jié)點(diǎn)的waitStatus的值等于0
    Node node = new Node(Thread.currentThread(), mode);
    //這時(shí)候tail和head都不為null了,所以就會(huì)進(jìn)入if分支
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    //由于tail和head都不為null,也就不會(huì)執(zhí)行到這里來了
    enq(node);
    return node;
}

這時(shí)候新進(jìn)來的線程就會(huì)從鏈表尾部插入,然后重新更新tail尾節(jié)點(diǎn)指向當(dāng)前線程

這時(shí)候addWaiter(Node.EXCLUSIVE), arg);就會(huì)返回一個(gè)Node節(jié)點(diǎn)了,這個(gè)Node節(jié)點(diǎn)就是當(dāng)前封裝了當(dāng)前線程的Node,比如現(xiàn)在第一個(gè)線程Thread-1獲取到鎖了,然后Thread-2進(jìn)來,那么Thread-2就要進(jìn)入隊(duì)列中進(jìn)行等待了,所以這時(shí)候返回的就是Thread-2這個(gè)Node節(jié)點(diǎn)。同理,第三個(gè)線程Thread-3進(jìn)來,那么這時(shí)候返回的就是Thread-3這個(gè)Node節(jié)點(diǎn)

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //Node.EXCLUSIVE表示是獨(dú)占模式
        selfInterrupt();
}

現(xiàn)在我們先分析第二個(gè)線程Thread-2。進(jìn)入acquireQueued()方法

final boolean acquireQueued(final Node node, int arg) {
    //此時(shí)Node節(jié)點(diǎn)就是Thread-2這個(gè)線程的Node
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            //拿到上一個(gè)節(jié)點(diǎn),因?yàn)門hread-2是第一個(gè)排隊(duì)的線程,所以他的前置節(jié)點(diǎn)就是頭節(jié)點(diǎn)
            final Node p = node.predecessor();
            //p是頭節(jié)點(diǎn),所以會(huì)進(jìn)入tryAcquire(arg)方法,這個(gè)方法就是再次去嘗試獲取鎖
            //但是頭節(jié)點(diǎn)就是個(gè)虛擬節(jié)點(diǎn),是不可能獲取到鎖的,所以不會(huì)進(jìn)入到if分支里面去
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            //此時(shí)會(huì)進(jìn)入到這個(gè)分支里面去,此時(shí)的p是頭節(jié)點(diǎn),node是Thread-2的Node節(jié)點(diǎn)
            //在第二次循環(huán)過后,shouldParkAfterFailedAcquire(p, node)會(huì)返回true
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
//第一次進(jìn)來,由于頭節(jié)點(diǎn)是剛初始化的,所以waitStatus=0,所以會(huì)進(jìn)入到else分支,修改頭節(jié)點(diǎn)waitStatus
//然后由于外面的是死循環(huán),所以會(huì)再次進(jìn)入,此時(shí)頭節(jié)點(diǎn)的waitStatus的值是Node.SIGNAL,所以會(huì)直接返回true
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        /*
         * This node has already set status asking a release
         * to signal it, so it can safely park.
         */
        return true;
    if (ws > 0) {
        /*
         * Predecessor was cancelled. Skip over predecessors and
         * indicate retry.
         */
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        //將頭節(jié)點(diǎn)的waitStatus修改成Node.SIGNAL
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

由于shouldParkAfterFailedAcquire(p, node)返回true了,所以就會(huì)繼續(xù)執(zhí)行 parkAndCheckInterrupt()方法了

private final boolean parkAndCheckInterrupt() {
    //將Thread-2線程阻塞掛起,這里為什么是this呢,因?yàn)楝F(xiàn)在執(zhí)行進(jìn)來的就是Thread-2線程
    LockSupport.park(this);
    return Thread.interrupted();
}

后續(xù)Thread-3線程進(jìn)來之后,也會(huì)在這里阻塞住,然后等待被喚醒

現(xiàn)在Thread-1線程業(yè)務(wù)執(zhí)行結(jié)束了,然后就要釋放鎖

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

我們首先看下tryRelease(arg)方法

protected final boolean tryRelease(int releases) {
    //將state的值減去1
    int c = getState() - releases;
    //判斷當(dāng)前線程是否是獲取鎖的那個(gè)線程
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //如果state的值減1之后變成0了,那么就將exclusiveOwnerThread設(shè)置成null,
    //因?yàn)槲覀僺tate的值就是等于1,所以c=0,就會(huì)進(jìn)入if分支里面去
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    //將state的值設(shè)置成c
    setState(c);
    //因?yàn)闀?huì)進(jìn)入if分支,所以此時(shí)free的值等于true
    return free;
}

所以tryRelease(arg)最后會(huì)返回true,于是就會(huì)進(jìn)入if分支里面去

public final boolean release(int arg) {
    if (tryRelease(arg)) { //返回true
        //拿到鏈表的頭節(jié)點(diǎn)
        Node h = head;
        //雖然在初始化的時(shí)候Node節(jié)點(diǎn)的waitStatus的值等于0,但是后面進(jìn)來的線程會(huì)將前置節(jié)點(diǎn)的該值修改成-1
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
//node是頭節(jié)點(diǎn)
private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);
    //拿到頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn),也就是Thread-2線程的節(jié)點(diǎn)
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        //最后喚醒Thread-2線程繼續(xù)執(zhí)行吧
        LockSupport.unpark(s.thread);
}

Thread-2之前是在acquireQueued()方法中被阻塞住的

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            //當(dāng)再次循環(huán)到這里來的時(shí)候,Thread-2的前置節(jié)點(diǎn)就是頭節(jié)點(diǎn),并且此時(shí)Thread-1線程
            //已經(jīng)釋放鎖了,所以此時(shí)tryAcquire(arg)嘗試去獲取鎖就能成功,所以會(huì)執(zhí)行if分支
            if (p == head && tryAcquire(arg)) {
                //將Thread-2的Node節(jié)點(diǎn)設(shè)置成頭節(jié)點(diǎn)
                setHead(node);
                //之前的頭節(jié)點(diǎn)都設(shè)置成null,可以被GC回收
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                //Thread-2阻塞在這里,被喚醒之后就會(huì)繼續(xù)執(zhí)行這個(gè)循環(huán)
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

所以最后Thread-2線程釋放鎖之后,此時(shí)鏈表的頭節(jié)點(diǎn)是Thread-2的Node節(jié)點(diǎn)了,Thread-2線程釋放鎖之后,也會(huì)喚醒Thread-3線程,然后繼續(xù)執(zhí)行。

以上就是深度解析Java中的ReentrantLock原理的詳細(xì)內(nèi)容,更多關(guān)于Java ReentrantLock原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot Redis批量存取數(shù)據(jù)的操作

    SpringBoot Redis批量存取數(shù)據(jù)的操作

    這篇文章主要介紹了SpringBoot Redis批量存取數(shù)據(jù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • MyBatis?多表聯(lián)合查詢及優(yōu)化方法

    MyBatis?多表聯(lián)合查詢及優(yōu)化方法

    大家都知道Hibernate 是全自動(dòng)的數(shù)據(jù)庫持久層框架,它可以通過實(shí)體來映射數(shù)據(jù)庫,通過設(shè)置一對(duì)多、多對(duì)一、一對(duì)一、多對(duì)多的關(guān)聯(lián)來實(shí)現(xiàn)聯(lián)合查詢,接下來通過本文給大家介紹MyBatis?多表聯(lián)合查詢及優(yōu)化,需要的朋友可以參考下
    2022-08-08
  • Java數(shù)組,去掉重復(fù)值、增加、刪除數(shù)組元素的方法

    Java數(shù)組,去掉重復(fù)值、增加、刪除數(shù)組元素的方法

    下面小編就為大家?guī)硪黄狫ava數(shù)組,去掉重復(fù)值、增加、刪除數(shù)組元素的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-10-10
  • Spring框架JavaMailSender發(fā)送郵件工具類詳解

    Spring框架JavaMailSender發(fā)送郵件工具類詳解

    這篇文章主要為大家詳細(xì)介紹了Spring框架JavaMailSender發(fā)送郵件工具類,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • Java面向?qū)ο笾畠?nèi)部類詳解

    Java面向?qū)ο笾畠?nèi)部類詳解

    在 Java 中,允許一個(gè)類的定義位于另一個(gè)類的內(nèi)部,前者稱為內(nèi)部類,后者稱為外部類。這篇文章將總結(jié)一下內(nèi)部類的使用,感興趣的可以了解一下
    2022-10-10
  • Java面試題解析之判斷以及防止SQL注入

    Java面試題解析之判斷以及防止SQL注入

    這篇文章主要介紹了Java面試題解析之判斷以及防止SQL注入,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-02-02
  • Java?binLog日志監(jiān)聽方式

    Java?binLog日志監(jiān)聽方式

    文章介紹了如何在Windows下開啟MySQL的binLog日志,并提供了一個(gè)Java代碼示例,演示如何監(jiān)聽指定的表并進(jìn)行處理邏輯
    2024-11-11
  • SpringCloud?Feign集成AOP的常見問題與解決

    SpringCloud?Feign集成AOP的常見問題與解決

    在使用?Spring?Cloud?Feign?作為微服務(wù)通信的工具時(shí),我們可能會(huì)遇到?AOP?不生效的問題,這篇文章將深入探討這一問題,給出幾種常見的場景,分析可能的原因,并提供解決方案,希望對(duì)大家有所幫助
    2023-10-10
  • MyBatis攔截器分表實(shí)踐分享

    MyBatis攔截器分表實(shí)踐分享

    部門內(nèi)有一些億級(jí)別核心業(yè)務(wù)表增速非???增量日均100W,但線上業(yè)務(wù)只依賴近一周的數(shù)據(jù),隨著數(shù)據(jù)量的迅速增長,慢SQL頻發(fā),數(shù)據(jù)庫性能下降,系統(tǒng)穩(wěn)定性受到嚴(yán)重影響,本篇文章,將分享如何使用MyBatis攔截器低成本的提升數(shù)據(jù)庫穩(wěn)定性,需要的朋友可以參考下
    2024-01-01
  • Java代碼實(shí)現(xiàn)矩形覆蓋實(shí)例

    Java代碼實(shí)現(xiàn)矩形覆蓋實(shí)例

    這篇文章主要介紹了Java代碼實(shí)現(xiàn)矩形覆蓋實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下
    2019-06-06

最新評(píng)論