一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專(zhuān)注于服務(wù)器技術(shù)及軟件下載分享
分類(lèi)導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

服務(wù)器之家 - 編程語(yǔ)言 - Java教程 - Java并發(fā)編程之ReentrantLock實(shí)現(xiàn)原理及源碼剖析

Java并發(fā)編程之ReentrantLock實(shí)現(xiàn)原理及源碼剖析

2022-01-13 01:00沒(méi)頭腦遇到不高興 Java教程

ReentrantLock 是常用的鎖,相對(duì)于Synchronized ,lock鎖更人性化,閱讀性更強(qiáng),文中將會(huì)詳細(xì)的說(shuō)明,請(qǐng)君往下閱讀

前面《Java并發(fā)編程之JUC并發(fā)核心AQS同步隊(duì)列原理剖析》介紹了AQS的同步等待隊(duì)列的實(shí)現(xiàn)原理及源碼分析,這節(jié)我們將介紹一下基于AQS實(shí)現(xiàn)的ReentranLock的應(yīng)用、特性、實(shí)現(xiàn)原理及源碼分析。

一、ReentrantLock簡(jiǎn)介

ReentrantLock位于Java的juc包里面,從JDK1.5開(kāi)始出現(xiàn),是基于AQS同步隊(duì)列的獨(dú)占模式實(shí)現(xiàn)的一種鎖。ReentrantLock使用起來(lái)比synchronized更加靈活,可以自己控制加鎖、解鎖的邏輯。ReentrantLock跟synchronized一樣也是可重入的鎖,提供了公平/非公平兩種模式:

  1. 公平鎖:多個(gè)線(xiàn)程競(jìng)爭(zhēng)鎖的時(shí)候,會(huì)先判斷等待隊(duì)列中是否有等待的線(xiàn)程節(jié)點(diǎn),如果有則當(dāng)前線(xiàn)程會(huì)進(jìn)行排隊(duì),鎖的獲取順序符合請(qǐng)求的絕對(duì)時(shí)間順序,也就是 FIFO
  2. 非公平鎖:當(dāng)前線(xiàn)程競(jìng)爭(zhēng)鎖的時(shí)候不管有沒(méi)有其他線(xiàn)程節(jié)點(diǎn)在排隊(duì),都會(huì)先通過(guò)CAS嘗試獲取鎖,獲取失敗了才會(huì)進(jìn)行排隊(duì)。

通過(guò)new ReentrantLock()的方式創(chuàng)建的是非公平鎖,要想創(chuàng)建公平鎖需要在構(gòu)造方法中指定new ReentrantLock(true)。ReentrantLock的常用方法如下:

  • void lock() 獲取鎖,如果當(dāng)前線(xiàn)程獲取鎖成功將返回,獲取鎖失敗線(xiàn)程將被阻塞、掛起
  • void lockInterruptibly() throws InterruptedException 可中斷的獲取鎖,和lock方法的不同之處在于該方法會(huì)響應(yīng)中斷,即在鎖的獲取過(guò)程中可以中斷當(dāng)前線(xiàn)程
  • boolean tryLock() 嘗試非阻塞的獲取鎖,方法會(huì)立即返回,獲取鎖成功返回true,否則返回false
  • boolean tryLock(long time, TimeUnit unit) throws InterruptedException 嘗試在指定超時(shí)時(shí)間內(nèi)獲取鎖,如果當(dāng)前線(xiàn)程獲取了鎖會(huì)立即返回true,如果被其他線(xiàn)程獲取了鎖則會(huì)被阻塞掛起,該方法會(huì)在下面三種情況下返回:1,在超時(shí)時(shí)間內(nèi)獲取了鎖,返回true;2,在超時(shí)時(shí)間內(nèi)線(xiàn)程被中斷;3,超時(shí)時(shí)間結(jié)束,返回false。
  • void unlock() 釋放鎖
  • Condition newCondition() 獲取等待通知組件,該組件與當(dāng)前的鎖綁定,當(dāng)前線(xiàn)程只有獲取了鎖,才能調(diào)用Condition的wait()方法,調(diào)用wait()方法后會(huì)釋放鎖

二、ReentrantLock使用

ReentrantLock的使用方式一般如下,一定要在finally里面進(jìn)行解鎖,防止程序出現(xiàn)異常無(wú)法解鎖

?
1
2
3
4
5
6
7
8
9
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
    System.out.println("獲取了鎖");
} catch (Exception e) {
    e.printStackTrace();
} finally {
    lock.unlock();
}

下面通過(guò)一個(gè)程序示例,演示一下ReentrantLock的使用:對(duì)同一個(gè)lock對(duì)象做多次加鎖,解鎖,演示一下ReentrantLock的鎖重入

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class ReentrantLockTest {
    private Integer counter = 0;
    private ReentrantLock lock = new ReentrantLock();
    
    public void modifyResources(String threadName){
        System.out.println("線(xiàn)程:--->"+threadName+"等待獲取鎖");
        lock.lock();
        System.out.println("線(xiàn)程:--->"+threadName+"第一次加鎖");
        counter++;
        System.out.println("線(xiàn)程:"+threadName+"做第"+counter+"件事");
        //重入該鎖,我還有一件事情要做,沒(méi)做完之前不能把鎖資源讓出去
        lock.lock();
        System.out.println("線(xiàn)程:--->"+threadName+"第二次加鎖");
        counter++;
        System.out.println("線(xiàn)程:"+threadName+"做第"+counter+"件事");
        lock.unlock();
        System.out.println("線(xiàn)程:"+threadName+"釋放一個(gè)鎖");
        lock.unlock();
        System.out.println("線(xiàn)程:"+threadName+"釋放一個(gè)鎖");
    }
 
    public static void main(String[] args) throws InterruptedException {
        ReentrantLockTest tp = new ReentrantLockTest();
 
        new Thread(()->{
            String threadName = Thread.currentThread().getName();
            tp.modifyResources(threadName);
        },"Thread:張三").start();
 
        new Thread(()->{
            String threadName = Thread.currentThread().getName();
            tp.modifyResources(threadName);
        },"Thread:李四").start();
 
        Thread.sleep(100);
    }
}

程序運(yùn)行輸出如下所示:上面代碼中l(wèi)ock加鎖兩次然后解鎖兩次,在張三線(xiàn)程兩次解鎖完成之前,李四線(xiàn)程一直在等待。ReentrantLock加鎖了幾次,就要解鎖相同的次數(shù)才可以釋放鎖。

線(xiàn)程:--->Thread:張三等待獲取鎖
線(xiàn)程:--->Thread:張三第一次加鎖
線(xiàn)程:Thread:張三做第1件事
線(xiàn)程:--->Thread:張三第二次加鎖
線(xiàn)程:--->Thread:李四等待獲取鎖
線(xiàn)程:Thread:張三做第2件事
線(xiàn)程:Thread:張三釋放一個(gè)鎖
線(xiàn)程:Thread:張三釋放一個(gè)鎖
線(xiàn)程:--->Thread:李四第一次加鎖
線(xiàn)程:Thread:李四做第3件事
線(xiàn)程:--->Thread:李四第二次加鎖
線(xiàn)程:Thread:李四做第4件事
線(xiàn)程:Thread:李四釋放一個(gè)鎖
線(xiàn)程:Thread:李四釋放一個(gè)鎖

三、ReentrantLock源碼分析

ReentrantLock實(shí)現(xiàn)了Lock接口,它有一個(gè)內(nèi)部類(lèi)Sync實(shí)現(xiàn)了前面介紹過(guò)的AbstractQueuedSynchronizer,而其公平鎖、非公平鎖分別通過(guò)Sync的子類(lèi)FairSync、NonFairSync(也是ReentrantLock的內(nèi)部類(lèi))實(shí)現(xiàn)。下面看下其UML圖

Java并發(fā)編程之ReentrantLock實(shí)現(xiàn)原理及源碼剖析

lock()方法調(diào)用時(shí)序圖如下:

Java并發(fā)編程之ReentrantLock實(shí)現(xiàn)原理及源碼剖析

前面《Java并發(fā)編程之JUC并發(fā)核心AQS同步隊(duì)列原理剖析》介紹AQS的時(shí)候說(shuō)過(guò),AbstractQueuedSynchronizer中有一個(gè)狀態(tài)變量state,在ReentrantLock中state等于0表示沒(méi)有線(xiàn)程獲取鎖,如果等于1說(shuō)明有線(xiàn)程獲取了鎖,如果大于1說(shuō)明獲取鎖的線(xiàn)程加鎖的次數(shù),加了幾次鎖就必須解鎖幾次,每次unlock解鎖state都會(huì)減1,減到0時(shí)釋放鎖。

1、非公平鎖源碼分析

前面一篇博客《Java并發(fā)編程之JUC并發(fā)核心AQS同步隊(duì)列原理剖析》對(duì)AQS介紹的已經(jīng)非常詳細(xì)了,所以下面源碼分析中牽涉AQS中的方法就不再進(jìn)行介紹了,想了解的話(huà)可以看下那篇博客。

先看下非公平鎖的加鎖lock方法,lock方法中調(diào)用了sync的lock方法,而sync對(duì)象時(shí)根據(jù)構(gòu)造ReentrantLock時(shí)是公平鎖(FairSync)還是非公平鎖(NonFairSync)。

?
1
2
3
public void lock() {
    sync.lock();
}

這里調(diào)用的是非公平鎖,所以我們看下 NonFairSync的lock方法:進(jìn)來(lái)時(shí)不管有沒(méi)有其他線(xiàn)程持有鎖或者等待鎖,會(huì)先調(diào)用AQS中的compareAndSetState方法嘗試獲取鎖,如果獲取失敗,會(huì)調(diào)用AQS中的acquire方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
final void lock() {
    /**
     * 第一步:直接嘗試加鎖
     * 與公平鎖實(shí)現(xiàn)的加鎖行為一個(gè)最大的區(qū)別在于,此處不會(huì)去判斷同步隊(duì)列(CLH隊(duì)列)中
     * 是否有排隊(duì)等待加鎖的節(jié)點(diǎn),上來(lái)直接加鎖(判斷state是否為0,CAS修改state為1)
     * ,并將獨(dú)占鎖持有者 exclusiveOwnerThread 屬性指向當(dāng)前線(xiàn)程
     * 如果當(dāng)前有人占用鎖,再?lài)L試去加一次鎖
     */
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        //AQS定義的方法,加鎖
        acquire(1);
}

下面看下acquire方法,會(huì)先調(diào)用NonFairSync類(lèi)中重寫(xiě)的tryAcquire方法嘗試獲取鎖,如果獲取鎖失敗會(huì)調(diào)用AQS中的acquireQueued方法進(jìn)行排隊(duì)、阻塞等處理。

?
1
2
3
4
5
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

下面看下NonFairSync類(lèi)中重寫(xiě)的tryAcquire方法,里面又調(diào)用了nonfairTryAcquire方法

?
1
2
3
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

下面看下nonfairTryAcquire方法:

  • 判斷state如果為0,通過(guò)CAS的方式嘗試獲取鎖,如果獲取鎖成功,則將當(dāng)前線(xiàn)程設(shè)置為獨(dú)占線(xiàn)程
  • 如果state不為0,則判斷當(dāng)前線(xiàn)程是否跟獨(dú)占線(xiàn)程時(shí)同一個(gè)線(xiàn)程,如果是同一個(gè)線(xiàn)程則將鎖的state加1,也就是鎖的重入次數(shù)加1
  • 否則獲取鎖失敗,返回false
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
final boolean nonfairTryAcquire(int acquires) {
    //acquires = 1
    final Thread current = Thread.currentThread();
    int c = getState();
    /**
     * 不需要判斷同步隊(duì)列(CLH)中是否有排隊(duì)等待線(xiàn)程
     * 判斷state狀態(tài)是否為0,不為0可以加鎖
     */
    if (c == 0) {
        //unsafe操作,cas修改state狀態(tài)
        if (compareAndSetState(0, acquires)) {
            //獨(dú)占狀態(tài)鎖持有者指向當(dāng)前線(xiàn)程
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    /**
     * state狀態(tài)不為0,判斷鎖持有者是否是當(dāng)前線(xiàn)程,
     * 如果是當(dāng)前線(xiàn)程持有 則state+1
     */
    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;
}

下面看下非公平鎖的解鎖過(guò)程:unlock方法中調(diào)用了AQS中的release方法

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

AQS中的release方法如下所示:會(huì)先調(diào)用AQS的子類(lèi)Sync中重寫(xiě)的tryRelease方法去釋放鎖,如果是否鎖成功,則喚醒同步隊(duì)列中head的后續(xù)節(jié)點(diǎn),后續(xù)節(jié)點(diǎn)線(xiàn)程被喚醒會(huì)去競(jìng)爭(zhēng)鎖。

?
1
2
3
4
5
6
7
8
9
public final boolean release(int arg) {
    if (tryRelease(arg)) {//釋放一次鎖
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);//喚醒后繼結(jié)點(diǎn)
        return true;
    }
    return false;
}

Sync中重寫(xiě)的tryRelease方法:

獲取當(dāng)前的state值,然后減1

判斷當(dāng)前線(xiàn)程是否是鎖的持有線(xiàn)程,如果不是會(huì)拋出異常。

如果state的值被減到了0,表示鎖已經(jīng)被釋放,會(huì)將獨(dú)占線(xiàn)程設(shè)置為空null,將state設(shè)置為0,返回true,否則返回false。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
 * 釋放鎖
 */
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;
}

2、公平鎖源碼分析

先看下公平鎖的加鎖lock方法,lock方法中調(diào)用了sync的lock方法,這里調(diào)用的是FairSync的lock方法。

?
1
2
3
public void lock() {
    sync.lock();
}

FairSync的lock方法直接調(diào)用了AQS中的acquire方法,沒(méi)有像非公平鎖先通過(guò)CAS的方式先去嘗試獲取鎖

?
1
2
3
final void lock() {
    acquire(1);
}

下面看下acquire方法,會(huì)先調(diào)用FairSync類(lèi)中重寫(xiě)的tryAcquire方法嘗試獲取鎖,如果獲取鎖失敗會(huì)調(diào)用AQS中的acquireQueued方法進(jìn)行排隊(duì)、阻塞等處理。

?
1
2
3
4
5
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

下面看下FairSync類(lèi)中重寫(xiě)的tryAcquire方法,這個(gè)方法跟NonFairSync的唯一區(qū)別就是state為0的時(shí)候,公平鎖會(huì)先通過(guò)hasQueuedPredecessors()方法判斷是否隊(duì)列中是否有等待的節(jié)點(diǎn),如果沒(méi)有才會(huì)嘗試通過(guò)CAS的方式去獲取鎖,非公平鎖不會(huì)判斷直接回嘗試獲取鎖。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        /**
         * 與非公平鎖中的區(qū)別,需要先判斷隊(duì)列當(dāng)中是否有等待的節(jié)點(diǎn)
         * 如果沒(méi)有則可以嘗試CAS獲取鎖
         */
        if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
            //獨(dú)占線(xiàn)程指向當(dāng)前線(xiàn)程
            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;
}

公平鎖的unlock方法與非公平鎖的代碼一樣,這里就不再介紹了。

到此這篇關(guān)于Java并發(fā)編程之ReentrantLock實(shí)現(xiàn)原理及源碼剖析的文章就介紹到這了,更多相關(guān)Java ReentrantLock內(nèi)容請(qǐng)搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://blog.csdn.net/u012988901/article/details/112557666

延伸 · 閱讀

精彩推薦
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關(guān)于小米推送Java代碼,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧...

    富貴穩(wěn)中求8032021-07-12
  • Java教程xml與Java對(duì)象的轉(zhuǎn)換詳解

    xml與Java對(duì)象的轉(zhuǎn)換詳解

    這篇文章主要介紹了xml與Java對(duì)象的轉(zhuǎn)換詳解的相關(guān)資料,需要的朋友可以參考下...

    Java教程網(wǎng)2942020-09-17
  • Java教程Java BufferWriter寫(xiě)文件寫(xiě)不進(jìn)去或缺失數(shù)據(jù)的解決

    Java BufferWriter寫(xiě)文件寫(xiě)不進(jìn)去或缺失數(shù)據(jù)的解決

    這篇文章主要介紹了Java BufferWriter寫(xiě)文件寫(xiě)不進(jìn)去或缺失數(shù)據(jù)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程20個(gè)非常實(shí)用的Java程序代碼片段

    20個(gè)非常實(shí)用的Java程序代碼片段

    這篇文章主要為大家分享了20個(gè)非常實(shí)用的Java程序片段,對(duì)java開(kāi)發(fā)項(xiàng)目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程升級(jí)IDEA后Lombok不能使用的解決方法

    升級(jí)IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級(jí),尋思已經(jīng)有好久沒(méi)有升過(guò)級(jí)了。升級(jí)完畢重啟之后,突然發(fā)現(xiàn)好多錯(cuò)誤,本文就來(lái)介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java實(shí)現(xiàn)搶紅包功能

    Java實(shí)現(xiàn)搶紅包功能

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)搶紅包功能,采用多線(xiàn)程模擬多人同時(shí)搶紅包,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程Java8中Stream使用的一個(gè)注意事項(xiàng)

    Java8中Stream使用的一個(gè)注意事項(xiàng)

    最近在工作中發(fā)現(xiàn)了對(duì)于集合操作轉(zhuǎn)換的神器,java8新特性 stream,但在使用中遇到了一個(gè)非常重要的注意點(diǎn),所以這篇文章主要給大家介紹了關(guān)于Java8中S...

    阿杜7482021-02-04
主站蜘蛛池模板: 性美国人xxxxx18 | 色444| 天天天天天天天操 | 国产成人亚洲精品91专区手机 | 成人看片免费无限观看视频 | 国产欧美日韩免费一区二区 | 国产香蕉一区二区在线观看 | 风间由美在线 | 亚洲国产午夜看片 | 亚洲国产成人久久综合一区 | 四虎影视在线看免费 720p | 欧美综合精品一区二区三区 | 成人免费草草视频 | 91视频破解版 | 大吊小说 | a国产在线| 亚洲国产果果在线播放在线 | 兽皇日本 | 99精品在线免费观看 | 小草观看免费高清视频 | 国产成人精品一区二区不卡 | 亚洲精品私拍国产福利在线 | 亚洲视频在线免费 | 国产精品欧美亚洲韩国日本99 | 波多野结衣178部中文字幕 | 舔比小说 | 九九热在线视频观看这里只有精品 | 久久国产精品高清一区二区三区 | 无码欧美喷潮福利XXXX | 亚洲高清国产拍精品动图 | 欧洲喷浆乌克兰 | 男女全黄h全肉细节文 | 亚洲精品国产SUV | 好大好硬好深好爽想要吃奶 | 非洲黑人又大粗gay 非洲黑人bbwbbwbbw | 国产成人啪精品午夜在线播放 | 国产成人精品在线 | 我的绝色岳每雯雯 | 亚洲欧美乱 | 狠狠干在线观看 | 成人看片免费无限观看视频 |