最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java并發(fā)編程之淺談ReentrantLock

 更新時間:2021年05月25日 15:12:49   作者:程大帥氣  
今天帶大家學(xué)習(xí)Java并發(fā)編程的相關(guān)知識,文中對Java ReentrantLock作了非常詳細(xì)的圖文示例,對正在學(xué)習(xí)java的小伙伴們有很好地幫助,需要的朋友可以參考下

一、首先看圖

在這里插入圖片描述

二、lock()跟蹤源碼

在這里插入圖片描述

這里對公平鎖和非公平鎖做了不同實現(xiàn),由構(gòu)造方法參數(shù)決定是否公平。

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

2.1 非公平鎖實現(xiàn)

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);
    }
}

代碼量很少。首先compareAndSetState(0, 1)通過CAS(期望值0,新值1,內(nèi)存值stateOffset)

  • 如果修改成功,即搶占到鎖,setExclusiveOwnerThread(Thread.currentThread());將AQS中的變量exclusiveOwnerThread設(shè)置為當(dāng)前搶占到鎖的線程,也就是圖中的ThreadA。
  • 若沒有搶占成功,證明此時鎖被占用,執(zhí)行方法acquire(1);。
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

這里主要看兩個方法tryAcquire(arg)acquireQueued(addWaiter(Node.EXCLUSIVE), arg)。當(dāng)滿足if條件后,會給當(dāng)前線程標(biāo)記一個interrupt狀態(tài)。

2.1.1 tryAcquire(arg)

這個方法又有多個實現(xiàn)。這里看NonfairSync非公平鎖。

在這里插入圖片描述

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

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;
 }

在這個方法中,還不死心,首先會判斷下AQS中的state是否為0,為0也就是說距離上次嘗試獲取鎖到現(xiàn)在準(zhǔn)備進(jìn)入隊列(雙向鏈表)中這段時間內(nèi),鎖已經(jīng)被釋放,可以重新CAS嘗試獲取鎖。

如果當(dāng)前鎖還是被持有狀態(tài),就是state!=0,就會判斷,當(dāng)前線程是不是當(dāng)前持有鎖的線程exclusiveOwnerThread,如果是,則state+1,從這里可以看出state表示的是重入次數(shù)。

全部不滿足,返回false。

2.1.2 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)

addWaiter

private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}

tryAcquire(arg)返回false,證明當(dāng)前線程還是沒有獲取到鎖。那么就要進(jìn)入隊列等待了,首先addWaiter方法,將當(dāng)前線程封裝成一個Node,如果pred不為空,則將當(dāng)前節(jié)點做鏈表的尾部插入,同時為了防止在此期間前序節(jié)點已經(jīng)不在隊列中了,也會運用CAS操作來執(zhí)行(期望值pred,新值node,內(nèi)存值tailOffset)。

如果前序節(jié)點為空,或者在CAS時發(fā)現(xiàn)前序節(jié)點已經(jīng)不存在了,則重新構(gòu)建鏈表,將當(dāng)前節(jié)點封裝的Node,加入到鏈表當(dāng)中。

private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

加入完成后,返回當(dāng)前node節(jié)點,進(jìn)入acquireQueued方法。

acquireQueued

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
        	//獲取到當(dāng)前node節(jié)點的上一個節(jié)點
            final Node p = node.predecessor();
            //如果當(dāng)前的上個節(jié)點就是頭節(jié)點,會再次嘗試獲取鎖
            if (p == head && tryAcquire(arg)) {
            	//獲取成功,將當(dāng)前節(jié)點置空,并成為新的頭節(jié)點
                setHead(node);
				//這個p已經(jīng)沒用了,防止內(nèi)存泄漏,直接指向null,下次GC時回收
                p.next = null; // help GC
                //不需要取消
                failed = false;
                //return false,不需要中斷當(dāng)前線程
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

這里是一個自旋操作,首先拿到當(dāng)前線程封裝節(jié)點的上一個節(jié)點,如果滿足第一個if條件if (p == head && tryAcquire(arg)),證明上個節(jié)點為頭節(jié)點,則此時當(dāng)前線程也會再次嘗試獲取鎖,獲取鎖成功,證明此時沒有別的線程在隊列中了,則將當(dāng)前node清空并設(shè)置為頭節(jié)點,返回不需要中斷當(dāng)前線程。

在第二個if條件中if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())。走到這里證明當(dāng)前線程不是第一個線程節(jié)點,或者沒有搶占到鎖,shouldParkAfterFailedAcquire這個方法見名知意,在搶占失敗后是否需要park阻塞,里面主要是用于清理雙向鏈表中被取消的節(jié)點線程和未被阻塞的節(jié)點線程。

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;//獲取前置節(jié)點的等待狀態(tài)
    if (ws == Node.SIGNAL)
		//前置節(jié)點的等待狀態(tài)為-1,表示前置節(jié)點在隊列中阻塞,那么當(dāng)前節(jié)點也需要被阻塞在隊列中
        return true;
    if (ws > 0) {
		//前置節(jié)點等待狀態(tài)大于0,此前置節(jié)點已經(jīng)被取消,循環(huán)遍歷清除所有已被取消的節(jié)點。
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
		//前置節(jié)點等待狀態(tài)小于等于0,且不等于-1,也就是沒有被阻塞也沒有被取消
		//則將前置節(jié)點設(shè)置為阻塞狀態(tài)。
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}
  • 前置節(jié)點的等待狀態(tài)為-1,表示前置節(jié)點在隊列中阻塞,那么當(dāng)前節(jié)點也需要被阻塞在隊列中
  • 前置節(jié)點等待狀態(tài)大于0,此前置節(jié)點已經(jīng)被取消,循環(huán)遍歷清除所有已被取消的節(jié)點。
  • 前置節(jié)點等待狀態(tài)小于等于0,且不等于-1,也就是沒有被阻塞也沒有被取消。則將前置節(jié)點設(shè)置為阻塞狀態(tài)。

到這里,基于非公平鎖的實現(xiàn)結(jié)束。

2.2 公平鎖實現(xiàn)

公平鎖和樂觀鎖的區(qū)別就在于,非公平鎖acquire(1)前會先嘗試獲取鎖,公平鎖直接acquire(1)。

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

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

2.2.1 tryAcquire(arg)

在tryAcquire中也和非公平鎖有一定的區(qū)別。在當(dāng)前鎖沒有被占有時。非公平鎖不用考慮目前AQS隊列中的排隊情況,直接通過CAS嘗試獲取鎖。公平鎖會看目前隊列的狀態(tài),再來決定是嘗試占有鎖還是在隊列中等待。

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;
}

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

相關(guān)文章

  • Java中的字符流FileReader與FileWriter詳解

    Java中的字符流FileReader與FileWriter詳解

    這篇文章主要介紹了Java中的字符流FileReader與FileWriter詳解,在Java中,使用Unicode約定存儲字符,字符流自動允許我們逐字符讀/寫數(shù)據(jù),有助于執(zhí)行16位Unicode的輸入和輸出,它是以reader和writer結(jié)尾的,需要的朋友可以參考下
    2023-10-10
  • Spring如何實現(xiàn)輸出帶動態(tài)標(biāo)簽的日志

    Spring如何實現(xiàn)輸出帶動態(tài)標(biāo)簽的日志

    文章介紹了如何通過動態(tài)標(biāo)簽日志實現(xiàn),解決了部分業(yè)務(wù)代碼在多個模塊中調(diào)用時日志無法直觀看出來源的問題,主要通過ThreadLocal存儲業(yè)務(wù)標(biāo)簽,并在日志輸出時插入該標(biāo)簽,實現(xiàn)日志的動態(tài)標(biāo)簽功能,感興趣的朋友一起看看吧
    2024-12-12
  • 使用Criteria進(jìn)行分組求和、排序、模糊查詢的實例

    使用Criteria進(jìn)行分組求和、排序、模糊查詢的實例

    這篇文章主要介紹了使用Criteria進(jìn)行分組求和、排序、模糊查詢的實例,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • idea復(fù)制module(項目)并在一個窗口展示的教程詳解

    idea復(fù)制module(項目)并在一個窗口展示的教程詳解

    這篇文章主要介紹了idea復(fù)制module(項目)并在一個窗口展示的方法,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-06-06
  • Java 實戰(zhàn)項目錘煉之網(wǎng)上圖書館管理系統(tǒng)的實現(xiàn)流程

    Java 實戰(zhàn)項目錘煉之網(wǎng)上圖書館管理系統(tǒng)的實現(xiàn)流程

    讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用Java+jsp+servlet+mysql+ajax實現(xiàn)一個網(wǎng)上圖書館管理系統(tǒng),大家可以在過程中查缺補漏,提升水平
    2021-11-11
  • 基于SpringBoot和Vue實現(xiàn)分片上傳系統(tǒng)

    基于SpringBoot和Vue實現(xiàn)分片上傳系統(tǒng)

    最近想做一個關(guān)于文件上傳的個人小網(wǎng)盤,一開始嘗試使用了OSS的方案,但是該方案對于大文件來說并不友好,所以開始嘗試分片上傳方案的探索,接下來小編給大家詳細(xì)的介紹一下如何基于SpringBoot和Vue實現(xiàn)分片上傳系統(tǒng),需要的朋友可以參考下
    2023-12-12
  • SpringBoot項目修改訪問端口和訪問路徑的方法

    SpringBoot項目修改訪問端口和訪問路徑的方法

    這篇文章主要介紹了SpringBoot項目修改訪問端口和訪問路徑的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-12-12
  • java中redis增刪查以及清理緩存的案例

    java中redis增刪查以及清理緩存的案例

    這篇文章主要介紹了java中redis增刪查以及清理緩存的案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • Eclipse中引入com.sun.image.codec.jpeg包報錯的完美解決辦法

    Eclipse中引入com.sun.image.codec.jpeg包報錯的完美解決辦法

    Java開發(fā)中對圖片的操作需要引入 com.sun.image.codec.jpeg,但有時引入這個包會報錯,利用下面的操作可以完成解決這個問題
    2018-02-02
  • Java中的@builder建造者模式詳細(xì)解析

    Java中的@builder建造者模式詳細(xì)解析

    這篇文章主要介紹了Java中的@builder建造者模式詳細(xì)解析,使用 @Builder 注解可以簡化手動編寫建造者模式的代碼,使代碼更加簡潔易讀,它可以自動生成鏈?zhǔn)秸{(diào)用的方法來設(shè)置對象的屬性,并且可以在需要時進(jìn)行可選屬性的設(shè)置,需要的朋友可以參考下
    2024-01-01

最新評論

乐亭县| 吐鲁番市| 丹阳市| 桓仁| 新郑市| 新宁县| 临洮县| 手游| 宜宾市| 林周县| 防城港市| 留坝县| 嘉义市| 苏尼特左旗| 尼木县| 玉山县| 新化县| 永丰县| 黄浦区| 积石山| 张掖市| 浦北县| 焉耆| 普安县| 瑞昌市| 永德县| 南召县| 赤壁市| 东乡族自治县| 东乡| 太白县| 长宁区| 昆山市| 新河县| 漯河市| 韶山市| 奎屯市| 嘉祥县| 西和县| 新郑市| 乌拉特后旗|