ReentrantLock可重入鎖、公平鎖非公平鎖區別與實現原理

炒燜煎糖板栗發表於2021-10-18

ReentrantLock是lock介面的一個實現類,裡面實現了可重入鎖和公平鎖非公平鎖

ReentrantLock公平鎖和不公平鎖實現原理

公平鎖會獲取鎖時會判斷阻塞佇列裡是否有執行緒再等待,若有獲取鎖就會失敗,並且會加入阻塞佇列

非公平鎖獲取鎖時不會判斷阻塞佇列是否有執行緒再等待,所以對於已經在等待的執行緒來說是不公平的,但如果是因為其它原因沒有競爭到鎖,它也會加入阻塞佇列

進入阻塞佇列的執行緒,競爭鎖時都是公平的,應為佇列為先進先出(FIFO)

預設實現的是非公平鎖

public ReentrantLock() {
    //非公平鎖
    sync = new NonfairSync();
}

還提供了另外一種方式,可傳入一個boolean值,true時為公平鎖,false時為非公平鎖

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

非公平鎖

非公平鎖獲取鎖nonfairTryAcquire方法,對鎖狀態進行了判斷,並沒有把鎖加入同步佇列中

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

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

公平鎖

程式碼和nonfairTryAcquire唯一的不同在於增加了hasQueuedPredecessors方法的判斷

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

       protected final boolean tryAcquire(int acquires) {
           //獲取當前執行緒
            final Thread current = Thread.currentThread();
            int c = getState();
            //判斷當前物件是否被持有
            if (c == 0) {
                //如果等待佇列為空 並且使用CAS獲取鎖成功   否則返回false然後從佇列中獲取節點
                if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {
                    //把當前執行緒持有
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
           //若被持有 判斷鎖是否是當前執行緒  可重入鎖的關鍵程式碼
            else if (current == getExclusiveOwnerThread()) {
                //計數加1 返回
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            //不是當前執行緒持有 執行
            return false;
        }
    }

acquire()獲取鎖

    public final void acquire(int arg) {
        //如果當前執行緒嘗試獲取鎖失敗並且 加入把當前執行緒加入了等待佇列 
        if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            //先中斷當前執行緒
            selfInterrupt();
    }

關鍵程式碼

就是tryAcquire方法中hasQueuedPredecessors判斷佇列是否有其他節點

public final boolean hasQueuedPredecessors() {
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

可重入性實現原理

線上程獲取鎖的時候,如果已經獲取鎖的執行緒是當前執行緒的話則直接再次獲取成功

由於鎖會被獲取n次,那麼只有鎖在被釋放同樣的n次之後,該鎖才算是完全釋放成功

1、獲取鎖方法

 protected final boolean tryAcquire(int acquires) {
           //獲取當前執行緒
            final Thread current = Thread.currentThread();
            int c = getState();
            //判斷當前物件是否被持有
            if (c == 0) {
              //...略
            }
           //若被持有 判斷鎖是否是當前執行緒  可重入鎖的關鍵程式碼
            else if (current == getExclusiveOwnerThread()) {
                //計數加1 返回
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            //不是當前執行緒持有 執行
            return false;
        }

每次如果獲取到的都是當前執行緒這裡都會計數加1

釋放鎖

protected final boolean tryRelease(int releases) {
    //每次釋放都減1
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //等於0才釋放鎖成功
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

如果鎖被獲取n次,也要釋放了n次,只有完全釋放才會返回false

相關文章