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

淺析Java?ReentrantLock鎖的原理與使用

 更新時(shí)間:2023年08月29日 08:31:23   作者:M`sakura~  
這篇文章主要為大家詳細(xì)介紹了Java中ReentrantLock鎖的原理與使用,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解下

一.  AQS內(nèi)部結(jié)構(gòu)介紹

JUC是Java中一個(gè)包   java.util.concurrent 。在這個(gè)包下,基本存放了Java中一些有關(guān)并發(fā)的類(lèi),包括并發(fā)工具,并發(fā)集合,鎖等。

AQS(抽象隊(duì)列同步器)是JUC下的一個(gè)基礎(chǔ)類(lèi),大多數(shù)的并發(fā)工具都是基于AQS實(shí)現(xiàn)的。

AQS本質(zhì)并沒(méi)有實(shí)現(xiàn)太多的業(yè)務(wù)功能,只是對(duì)外提供了三點(diǎn)核心內(nèi)容,來(lái)幫助實(shí)現(xiàn)其他的并發(fā)內(nèi)容。

三點(diǎn)核心內(nèi)容:

int state

  • 比如ReentrantLock或者ReentrantReadWriteLock, 它們獲取鎖的方式,都是對(duì)state變量做修改實(shí)現(xiàn)的。
  • 比如CountDownLatch基于state作為計(jì)數(shù)器,同樣的Semaphore也是用state記錄資源個(gè)數(shù)。

Node對(duì)象組成的雙向鏈表(AQS中)

比如ReentrantLock,有一個(gè)線(xiàn)程沒(méi)有拿到鎖資源,當(dāng)線(xiàn)程需要等待,則需要將線(xiàn)程封裝為Node對(duì)象,將Node添加到雙向鏈表,將線(xiàn)程掛起,等待即可。

Node對(duì)象組成的單向鏈表(AQS中的ConditionObject類(lèi)中)

比如ReentrantLock,一個(gè)線(xiàn)程持有鎖資源時(shí),執(zhí)行了await方法(類(lèi)比synchronized鎖執(zhí)行對(duì)象的wait方法),此時(shí)這個(gè)線(xiàn)程需要封裝為Node對(duì)象,并添加到單向鏈表。

二.  Lock鎖和AQS關(guān)系

ReentrantLock就是基于AQS實(shí)現(xiàn)的。ReentrantLock類(lèi)中維護(hù)這個(gè)一個(gè)內(nèi)部抽象類(lèi)Sync,他繼承了AQS類(lèi)。ReentrantLock的lock和unlock方法就是調(diào)用的Sync的方法。

AQS流程(簡(jiǎn)述)

1. 當(dāng)new了一個(gè)ReentrantLock時(shí),AQS默認(rèn)state值為0, head 和 tail 都為null;

2. A線(xiàn)程執(zhí)行l(wèi)ock方法,獲取鎖資源。

3. A線(xiàn)程將state通過(guò)cas操作從0改為1,代表獲取鎖資源成功。

4. B線(xiàn)程要獲取鎖資源時(shí),鎖資源被A線(xiàn)程持有。

5. B線(xiàn)程獲取鎖資源失敗,需要添加到雙向鏈表中排隊(duì)。

6. 掛起B(yǎng)線(xiàn)程,等待A線(xiàn)程釋放鎖資源,再喚醒掛起的B線(xiàn)程。

7. A線(xiàn)程釋放鎖資源,將state從1改為0,再喚醒head.next節(jié)點(diǎn)。

8. B線(xiàn)程就可以重新嘗試獲取鎖資源。

注: 修改AQS雙向鏈表時(shí)要保證一個(gè)私有屬性變化和兩個(gè)共有屬性變化,只需要讓tail變化保證原子性即可。不能先改tail(會(huì)破壞雙向鏈表)

三.  AQS - Lock鎖的tryAcquire方法

ReentrantLock中的lock方法實(shí)際是執(zhí)行的Sync的lock方法。

Sync是一個(gè)抽象類(lèi),繼承了AQS

Sync有兩個(gè)子類(lèi)實(shí)現(xiàn):

  • FairSync: 公平鎖
  • NonFairSync: 非公平鎖

Sync的lock方法實(shí)現(xiàn):

//  非公平鎖
final void lock() {
    //  CAS操作,嘗試將state從0改為1
    //  成功就拿到鎖資源, 失敗執(zhí)行acquire方法
    if (compareAndSetState(0, 1))
     // 成功就設(shè)置互斥鎖的為當(dāng)前線(xiàn)程擁有
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}
//  公平鎖
final void lock() {
    acquire(1);
}

如果CAS操作沒(méi)有成功,需要執(zhí)行acquire方法走后續(xù)

acquire方法是AQS提供的,公平和非公平都是走的這個(gè)方法

public final void acquire(int arg) {
    //  1. tryAcquire方法: 再次嘗試拿鎖
    //  2. addWaiter方法: 沒(méi)有獲取到鎖資源,去排隊(duì)
    //  3. acquireQueued方法:掛起線(xiàn)程和后續(xù)被喚醒繼續(xù)獲取鎖資源的邏輯
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
     // 如果這個(gè)過(guò)程中出現(xiàn)中斷,在整個(gè)過(guò)程結(jié)束后再自我中斷 
        selfInterrupt();
}

在AQS中tryAcquire是沒(méi)有具體實(shí)現(xiàn)邏輯的,AQS直接在tryAcquire方法中拋出異常

在公平鎖和非公平鎖中有自己的實(shí)現(xiàn)。

非公平鎖tryAcquire方法

//  非公平鎖
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}
//  非公平鎖再次嘗試拿鎖 (注:該方法屬于Sync類(lèi)中)
final boolean nonfairTryAcquire(int acquires) {
    //  獲取當(dāng)前線(xiàn)程對(duì)象
    final Thread current = Thread.currentThread();
    //  獲取state狀態(tài)
    int c = getState();
    //  state是不是沒(méi)有線(xiàn)程持有鎖資源,可以嘗試獲取鎖
    if (c == 0) {
        //  再次CAS操作嘗試修改state狀態(tài)從0改為1
        if (compareAndSetState(0, acquires)) {
            //  成功就設(shè)置互斥鎖的為當(dāng)前線(xiàn)程擁有
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //  鎖資源是否被當(dāng)前線(xiàn)程所持有 (可重入鎖)
    else if (current == getExclusiveOwnerThread()) {
        //  持有鎖資源為當(dāng)前, 則對(duì)state + 1
        int nextc = c + acquires;
        //  健壯性判斷
        if (nextc < 0) // overflow
            //  超過(guò)最大鎖重入次數(shù)會(huì)拋異常(幾率很小,理論上存在)
            throw new Error("Maximum lock count exceeded");
        //  設(shè)置state狀態(tài),代表鎖重入成功
        setState(nextc);
        return true;
    }
    return false;
}

公平鎖tryAcquire方法

//  公平鎖
protected final boolean tryAcquire(int acquires) {
    //  獲取當(dāng)前線(xiàn)程對(duì)象
    final Thread current = Thread.currentThread();
    //  獲取state狀態(tài)
    int c = getState();
    //  state是不是沒(méi)有線(xiàn)程持有鎖資源
    if (c == 0) {
        //  當(dāng)前鎖資源沒(méi)有被其他線(xiàn)程持有
        //  hasQueuedPredecessors方法: 鎖資源沒(méi)有被持有,進(jìn)入隊(duì)列排隊(duì)
        //  排隊(duì)規(guī)則:
        //  1. 檢查隊(duì)列沒(méi)有線(xiàn)程排隊(duì),搶鎖。
        //  2. 檢查隊(duì)列有線(xiàn)程排隊(duì),查看當(dāng)前線(xiàn)程是否排在第一位,如果是搶鎖,否則入隊(duì)列(注:該方法只是判斷,沒(méi)有真正入隊(duì)列)
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            //  再次CAS操作嘗試, 成功就設(shè)置互斥鎖的為當(dāng)前線(xiàn)程擁有
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //  鎖資源是否被當(dāng)前線(xiàn)程所持有 (可重入鎖)
    else if (current == getExclusiveOwnerThread()) {
        //  持有鎖資源為當(dāng)前, 則對(duì)state + 1
        int nextc = c + acquires;
        //  健壯性判斷
        if (nextc < 0)
            //  超過(guò)最大鎖重入次數(shù)會(huì)拋異常(幾率很小,理論上存在)
            throw new Error("Maximum lock count exceeded");
        //  設(shè)置state狀態(tài),代表鎖重入成功
        setState(nextc);
        return true;
    }
    return false;
}

四.  AQS的addWaiter方法

 addWaiter方法,就是將當(dāng)前線(xiàn)程封裝為Node對(duì)象,并且插入到AQS的雙向鏈表。

//  線(xiàn)程入隊(duì)列排隊(duì)
private Node addWaiter(Node mode) {
    //  將當(dāng)前對(duì)象封裝為Node對(duì)象 
    //  Node.EXCLUSIVE 表示互斥  Node.SHARED 表示共享
    Node node = new Node(Thread.currentThread(), mode);
    // 獲取tail節(jié)點(diǎn)
    Node pred = tail;
    //  判斷雙向鏈表隊(duì)列有沒(méi)有初始化
    if (pred != null) {
        //  將當(dāng)前線(xiàn)程封裝的Node節(jié)點(diǎn)prev屬性指向tail尾節(jié)點(diǎn)
        node.prev = pred;
        //  通過(guò)CAS操作設(shè)置當(dāng)前線(xiàn)程封裝的Node節(jié)點(diǎn)為尾節(jié)點(diǎn)
        if (compareAndSetTail(pred, node)) {
            //  成功則將上一個(gè)尾節(jié)點(diǎn)的next屬性指向當(dāng)前線(xiàn)程封裝的Node節(jié)點(diǎn)
            pred.next = node;
            return node;
        }
    }
    //  沒(méi)有初始化head 和 tail 都等于null
    //  enq方法: 插入雙向鏈表和初始化雙向鏈表
    enq(node);
    //  完成節(jié)點(diǎn)插入
    return node;
}
//  插入雙向鏈表和初始化雙向鏈表
private Node enq(final Node node) {
    //  死循環(huán) 
    for (;;) {
        //  獲取當(dāng)前tail節(jié)點(diǎn)
        Node t = tail;
        //  判斷尾節(jié)點(diǎn)是否初始
        if (t == null) { // Must initialize
            //  通過(guò)CAS操作初始化初始化一個(gè)虛擬的Node節(jié)點(diǎn),賦給head節(jié)點(diǎn)
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            //  完成當(dāng)前線(xiàn)程N(yùn)ode節(jié)點(diǎn)加入AQS雙向鏈表的過(guò)程
            //  當(dāng)前線(xiàn)程封裝的Node的上一個(gè)prev屬性指向tail節(jié)點(diǎn)
            //  流程: 1. prev(私有)  --->  2. tail(共有)  ---> 3. next (共有)
            node.prev = t;
            //  通過(guò)CAS操作修改tail尾節(jié)點(diǎn)指向當(dāng)前線(xiàn)程封裝的Node
            if (compareAndSetTail(t, node)) {
                //  將當(dāng)前線(xiàn)程封裝的Node節(jié)點(diǎn)賦給上一個(gè)Node的下一個(gè)next屬性
                t.next = node;
                return t;
            }
        }
    }
}

五.  AQS的acquireQueued方法

acquireQueued方法主要就是線(xiàn)程掛起以及重新嘗試獲取鎖資源的地方

重新獲取鎖資源主要有兩種情況:

  • 上來(lái)就排在head.next,就回去嘗試拿鎖
  • 喚醒之后嘗試拿鎖
//  當(dāng)前線(xiàn)程N(yùn)ode添加到AQS隊(duì)列后續(xù)操作
final boolean acquireQueued(final Node node, int arg) {
    //  標(biāo)記,記錄拿鎖狀態(tài)   失敗
    boolean failed = true;
    try {
        // 中斷狀態(tài)
        boolean interrupted = false;
        //  死循環(huán)
        for (;;) {
            //  獲取當(dāng)前節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn)    prev
            final Node p = node.predecessor();
            //  判斷當(dāng)前節(jié)點(diǎn)是否是head,是則代表當(dāng)前節(jié)點(diǎn)排在第一位
            //  如果是第一位,執(zhí)行tryAcquire方法嘗試拿鎖
            if (p == head && tryAcquire(arg)) {
                //  都成功,代表拿到鎖資源
                //  將當(dāng)前線(xiàn)程N(yùn)ode設(shè)置為head節(jié)點(diǎn),同時(shí)將Node的thread 和 prev屬性設(shè)置為null
                setHead(node);
                //  將上一個(gè)head的next屬性設(shè)置為null,等待GC回收
                p.next = null; // help GC
                //  拿鎖狀態(tài)  成功
                failed = false;
                //  返回中斷狀態(tài)
                return interrupted;
            }
            //  沒(méi)有獲取到鎖 --- 嘗試掛起線(xiàn)程
            //  shouldParkAfterFailedAcquire方法: 掛起線(xiàn)程前的準(zhǔn)備
            //  parkAndCheckInterrupt方法: 掛起當(dāng)前線(xiàn)程
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                //  設(shè)置中斷線(xiàn)程狀態(tài)
                interrupted = true;
        }
    } finally {
        //  取消節(jié)點(diǎn)
        if (failed)
            cancelAcquire(node);
    }
}
//  檢查并更新無(wú)法獲取鎖節(jié)點(diǎn)的狀態(tài)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    //  獲取上一個(gè)節(jié)點(diǎn)的ws狀態(tài)
    /**
    * SIGNAL(-1)   表示當(dāng)前節(jié)點(diǎn)釋放鎖的時(shí)候,需要喚醒下一個(gè)節(jié)點(diǎn)?;蛘哒f(shuō)后繼節(jié)點(diǎn)在等待當(dāng)前節(jié)點(diǎn)喚醒,后繼節(jié)點(diǎn)入隊(duì)時(shí)候,會(huì)將前驅(qū)節(jié)點(diǎn)更新給signal。
    * CANCELLED(1)  表示當(dāng)前節(jié)點(diǎn)已取消調(diào)度。當(dāng)timeout或者中斷情況下,會(huì)觸發(fā)變更為此狀態(tài),進(jìn)入該狀態(tài)后的節(jié)點(diǎn)不再變化。
    * CONDITION(-2)  當(dāng)其他線(xiàn)程調(diào)用了condition的signal方法后,condition狀態(tài)的節(jié)點(diǎn)會(huì)從等待隊(duì)列轉(zhuǎn)移到同步隊(duì)列中,等待獲取同步鎖。
    * PROPAGATE(-3)   表示共享模式下,前驅(qū)節(jié)點(diǎn)不僅會(huì)喚醒其后繼節(jié)點(diǎn),同時(shí)也可能喚醒后繼的后繼節(jié)點(diǎn)。
    * 默認(rèn)(0) 新節(jié)點(diǎn)入隊(duì)時(shí)候的默認(rèn)狀態(tài)。
    */
    int ws = pred.waitStatus;
    //  判斷上個(gè)節(jié)點(diǎn)ws狀態(tài)是否是 -1, 是則掛起    
    if (ws == Node.SIGNAL)
        return true;
    if (ws > 0) {
        /** 
        * 判斷上個(gè)節(jié)點(diǎn)是否是取消或者其他狀態(tài)。
        * 向前找到不是取消狀態(tài)的節(jié)點(diǎn),修改ws狀態(tài)。
        * 注意:那些放棄的結(jié)點(diǎn),由于被自己“加塞”到它們前邊,它們相當(dāng)于形成一個(gè)無(wú)引用鏈,
        * 稍后就會(huì)被GC回收,這個(gè)操作實(shí)際是把隊(duì)列中的cancelled節(jié)點(diǎn)剔除掉。
        */
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        //  如果前驅(qū)節(jié)點(diǎn)正常,那就把上一個(gè)節(jié)點(diǎn)的狀態(tài)通過(guò)CAS的方式設(shè)置成-1
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}
//  掛起當(dāng)前線(xiàn)程
private final boolean parkAndCheckInterrupt() {
    //  掛起當(dāng)前線(xiàn)程
    LockSupport.park(this);
    //  返回中斷標(biāo)志
    return Thread.interrupted();
}

六.  AQS的Lock鎖的release方法

//  互斥鎖模式   解鎖
public final boolean release(int arg) {
    //  嘗試是否可以解鎖
    if (tryRelease(arg)) {
        Node h = head;
        //  判斷雙鏈表是否存在線(xiàn)程排隊(duì)
        if (h != null && h.waitStatus != 0)
            //  喚醒后續(xù)線(xiàn)程
            unparkSuccessor(h);
        return true;
    }
    return false;
}
//  嘗試是否可以解鎖
protected final boolean tryRelease(int releases) {
    //  鎖狀態(tài) =  狀態(tài) - 1
    int c = getState() - releases;
    //  判斷鎖是是否是當(dāng)前線(xiàn)程持有
    if (Thread.currentThread() != getExclusiveOwnerThread())
        //  當(dāng)前線(xiàn)程沒(méi)有持有拋出異常
        throw new IllegalMonitorStateException();
    boolean free = false;
    //  當(dāng)前鎖狀態(tài)變?yōu)?,則清空鎖歸屬線(xiàn)程
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    //  設(shè)置鎖狀態(tài)為0
    setState(c);
    return free;
}
//  喚醒線(xiàn)程
private void unparkSuccessor(Node node) {
    //  獲取頭節(jié)點(diǎn)的狀態(tài)
    int ws = node.waitStatus;
    if (ws < 0)
        //  通過(guò)CAS將頭節(jié)點(diǎn)的狀態(tài)設(shè)置為初始狀態(tài)
        compareAndSetWaitStatus(node, ws, 0);
    //  后繼節(jié)點(diǎn)
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        //  從尾節(jié)點(diǎn)開(kāi)始往前遍歷,尋找離頭節(jié)點(diǎn)最近的等待狀態(tài)正常的節(jié)點(diǎn)
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        //  真正的喚醒操作
        LockSupport.unpark(s.thread);
}

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

相關(guān)文章

最新評(píng)論