1. 程式人生 > >Java並發(九):重入鎖 ReentrantLock

Java並發(九):重入鎖 ReentrantLock

阻塞隊列 輪詢 cee condition 源碼分析 interrupt 同步 iter 投票

一、ReentrantLock類結構

public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync; // 鎖 大部分功能都是委托給Sync來實現的
    abstract static class Sync extends AbstractQueuedSynchronizer {}
    static final class FairSync extends Sync {}
    static final class NonfairSync extends Sync {}
}

二、以NonfairSync為例解析重入鎖

獲取鎖標誌:

(NonfairSync extends Sync extends AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer)

1.AbstractQueuedSynchronizer.state>0(0代表沒有被占用,大於0代表有線程持有當前鎖(鎖可以重入,每次重入都+1))

2.AbstractOwnableSynchronizer.exclusiveOwnerThread == Thread.currentThread()

獲取鎖:

public static
void main(String[] args) { ReentrantLock lock = new ReentrantLock();// 默認是非公平鎖 lock.lock(); } // ReentrantLock public void lock() { sync.lock(); } // NonfairSync final void lock() { if (compareAndSetState(0, 1)) // 嘗試獲取鎖 setExclusiveOwnerThread(Thread.currentThread()); //
如果拿到鎖就設置當前線程 else acquire(1); } // AbstractQueuedSynchronizer public final void acquire(int arg) { if (!tryAcquire(arg) && // 嘗試獲取鎖 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 沒有獲取到鎖,將線程加入同步隊列(參考上一篇AbstractQueuedSynchronizer) selfInterrupt(); } // NonfairSync protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } /** * 獲取鎖標誌: * (NonfairSync extends Sync extends AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer) * 1.AbstractQueuedSynchronizer.state>0(0代表沒有被占用,大於0代表有線程持有當前鎖(鎖可以重入,每次重入都+1)) * 2.AbstractOwnableSynchronizer.exclusiveOwnerThread == Thread.currentThread() * Sync(NonfairSync沒有重寫nonfairTryAcquire) */ 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; }

釋放鎖:

    // ReentrantLock
    public void unlock() {
        sync.release(1);
    }
    
    // AbstractQueuedSynchronizer
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h); // 喚醒隊列下一個節點線程 參考上一篇:AbstractQueuedSynchronizer
            return true;
        }
        return false;
    }
    
    // Sync
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false; // 重入鎖,直到state==0才算釋放
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }

三、公平鎖與非公平鎖

    // FairSync(NonfairSync會先嘗試拿鎖,FairSync不會)
    final void lock() {
        acquire(1);
    }

    // FairSync
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (!hasQueuedPredecessors() && // CLH隊列為空或者隊列頭結點是當前線程節點 才能獲得鎖
                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;
    }
    
    /**
     * AbstractQueuedSynchronizer
     * true - CLH隊列為空或者隊列頭結點是當前線程節點
     */
    public final boolean hasQueuedPredecessors() {
        Node t = tail;
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

以上代碼可以看出,公平鎖和非公平鎖只有兩處不同:

(1)非公平鎖在調用 lock 後,首先就會調用 CAS 進行一次搶鎖,如果這個時候恰巧鎖沒有被占用,那麽直接就獲取到鎖。

(2)非公平鎖在 CAS 失敗後,和公平鎖一樣都會進入到 tryAcquire 方法。在 tryAcquire 方法中,如果發現鎖這個時候被釋放了(state == 0),非公平鎖會直接 CAS 搶鎖,而公平鎖會判斷等待隊列是否有線程處於等待狀態,如果有則不去搶鎖,乖乖排到後面。

公平鎖和非公平鎖就這兩點區別,如果這兩次 CAS 都不成功,那麽後面非公平鎖和公平鎖是一樣的,都要進入到阻塞隊列等待喚醒。

因此,非公平鎖會有更好的性能,因為它的吞吐量比較大。當然,非公平鎖讓獲取鎖的時間變得更加不確定,可能會導致在阻塞隊列中的線程長期處於饑餓狀態。

四、ReentrantLock優勢

ReentrantLock與synchronized具有相同的功能和內存語義。

1、與synchronized相比,ReentrantLock提供了更多,更加全面的功能,具備更強的擴展性。例如:時間鎖等候,可中斷鎖等候,鎖投票。

2、ReentrantLock還提供了條件Condition,對線程的等待、喚醒操作更加詳細和靈活,所以在多個條件變量和高度競爭鎖的地方,ReentrantLock更加適合。

3、ReentrantLock提供了可輪詢的鎖請求。它會嘗試著去獲取鎖,如果成功則繼續,否則可以等到下次運行時處理,而synchronized則一旦進入鎖請求要麽成功要麽阻塞,所以相比synchronized而言,ReentrantLock會不容易產生死鎖些。

4、ReentrantLock支持更加靈活的同步代碼塊,但是使用synchronized時,只能在同一個synchronized塊結構中獲取和釋放。註:ReentrantLock的鎖釋放一定要在finally中處理,否則可能會產生嚴重的後果。

5、ReentrantLock支持中斷處理,且性能較synchronized會好些。

參考資料 / 相關推薦

【死磕Java並發】—–J.U.C之重入鎖:ReentrantLock

一行一行源碼分析清楚AbstractQueuedSynchronizer

Java並發(八):AbstractQueuedSynchronizer

Java並發(九):重入鎖 ReentrantLock