溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

多線程(十、AQS原理-ReentrantLock公平鎖)

發(fā)布時間:2020-07-06 06:48:12 來源:網絡 閱讀:460 作者:shayang88 欄目:編程語言

ReentrantLock介紹

ReentrantLock 基于AQS實現(xiàn)了公平和非公平的獨占鎖功能。

ReentrantLock定義AQS的同步狀態(tài)(synchronization state)如下:

State為0表示鎖可用;為1表示被占用;為N表示鎖重入的次數,是獨占資源。

ReentrantLock實現(xiàn)公平鎖原理

案例代碼如下:

1、啟動文件

public class Main {

    public static void main(String[] args) throws ParseException {

        ReentrantLock lock = new ReentrantLock(true);
        Thread t1 = new Thread(new Task(lock),"Thread-1");
        Thread t2 = new Thread(new Task(lock),"Thread-2");
        Thread t3 = new Thread(new Task(lock),"Thread-3");

        t1.start();
        t2.start();
        t3.start();
    }
}

2、Task

import java.util.concurrent.locks.ReentrantLock;

public class Task implements Runnable{

    private ReentrantLock lock;

    public Task(ReentrantLock lock) {
        this.lock = lock;
    }

    @Override
    public void run() {

        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "獲取到鎖....");
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + "釋放鎖....");
            lock.unlock();
        }
    }
}

3、執(zhí)行結果:
多線程(十、AQS原理-ReentrantLock公平鎖)

案例分析

1、Thread-1調用lock方法

多線程(十、AQS原理-ReentrantLock公平鎖)

ReentrantLock內部繼承AQS實現(xiàn)了1個抽象類Sync

多線程(十、AQS原理-ReentrantLock公平鎖)

繼承于Sync實現(xiàn)了2個內部類FairSync(公平的)和NonfairSync(非公平的)

此時調用FairSync的lock方法

多線程(十、AQS原理-ReentrantLock公平鎖)

acquire方法來自AQS,注意參數是1

多線程(十、AQS原理-ReentrantLock公平鎖)

tryAcquire是ReentrantLock自己實現(xiàn)的,嘗試獲取鎖
protected final boolean tryAcquire(int acquires) { //參數是1
        final Thread current = Thread.currentThread(); //當前線程
        int c = getState(); //獲取當前同步狀態(tài)
        if (c == 0) { //如果是0,則鎖沒有被占用
            //等待隊列中,前面沒有其他等待的線程,則用CAS的方法更新同步狀態(tài)state
            if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current); //成功的話,則設置鎖的占有線程為當前線程
                return true; //返回獲取資源成功
            }
        }
        //如果鎖已經被占用,則判斷是不是自己占用的
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;//如果是自己占用的,則是重入,增加state值,累加1
            if (nextc < 0) //重入次數過大,拋出異常
                throw new Error("Maximum lock count exceeded");
            setState(nextc); //設置state值
            return true; //重入返回ture
        }
        return false;//沒有獲取資源返回false
    }
Thread-1獲取了鎖資源,沒有釋放。

2、Thread-2,開始請求資源,調用lock,此時鎖資源還被Thread-1占用

多線程(十、AQS原理-ReentrantLock公平鎖)

addWaiter方法:
private Node addWaiter(Node mode) {
        //把當前線程包裝成節(jié)點,準備放入等待隊列
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        //嘗試直接把節(jié)點設置成隊尾,否則執(zhí)行enq
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;//當前節(jié)點的上一個節(jié)點是之前的隊尾節(jié)點
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //當前節(jié)點插入隊尾
        enq(node);
        return node;
    }
enq自旋+初始化等待隊列,并返回Thread-2節(jié)點
private Node enq(final Node node) {
        //采用自旋,保證節(jié)點插入
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize 如果隊列為空,則創(chuàng)建一個空的節(jié)點,設置為頭尾節(jié)點
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) { //隊列不為空,追加到隊尾
                    t.next = node;
                    return t;
                }
            }
        }
    }
然后對Thread-2包裝節(jié)點執(zhí)行acquireQueued
final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                //判斷節(jié)點的前任節(jié)點是不是頭節(jié)點,頭節(jié)點是一個空節(jié)點
                final Node p = node.predecessor();
                //如果是頭節(jié)點,則說明當前節(jié)點是隊列里的第一個節(jié)點,首節(jié)點。
                //則嘗試獲取鎖資源,此處因為Thread-1占用著資源,則失敗
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //失敗之后,則判斷當前節(jié)點線程Thread-2是不是可以阻塞
                if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
是否阻塞shouldParkAfterFailedAcquire
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;//前驅節(jié)點的狀態(tài)
        if (ws == Node.SIGNAL) //如果是SIGNAL,則說明前驅節(jié)點狀態(tài)可以喚醒后繼節(jié)點,可以阻塞
            /*
             * 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; //只有CANCELLED狀態(tài)大于0,則把取消狀態(tài)的節(jié)點從隊列刪除
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);//設置前驅節(jié)點為SIGNAL狀態(tài)
        }
        return false;
    }
如果可以阻塞,則調用parkAndCheckInterrupt,阻塞線程,至此Thread-2進入隊列,并阻塞了,耐心等待

3、Thread-3同Thread-2,略過

4、Thread-1釋放鎖資源

多線程(十、AQS原理-ReentrantLock公平鎖)

release方法:

多線程(十、AQS原理-ReentrantLock公平鎖)

tryRelease

多線程(十、AQS原理-ReentrantLock公平鎖)

然后通過unparkSuccessor,喚醒首節(jié)點,保證公平策略。

至此Thread-1釋放完成,Thread-2可以獲得資源,依次類推。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI