并發(fā)編程之鎖(三)--ReentrantLock

前言

上一篇中已經分析了關鍵的AQS抽象隊列同步器,下面我們來看一下使用AQS來實現的可重入獨占鎖ReentrantLock。
ReentrantLock是可重入的獨占鎖,同時只有一個線程可以獲取該鎖,下面我們來看下其類圖:


ReentrantLock結構圖

Sync抽象類

Sync是ReentrantLock的靜態(tài)內部類,繼承自AbstractQueuedSynchronizer。它使用AQS的state字段來表示當前鎖的持有數量,從而實現可重入的特性。

  • #lock()
/**
 * Performs {@link Lock#lock}. The main reason for subclassing
 * is to allow fast path for nonfair version.
 */
abstract void lock();

允許子類實現快速獲得非公平鎖的邏輯。

  • #nonfairTryAcquire(int acquires)
final boolean nonfairTryAcquire(int acquires) {
    //1.當前線程
    final Thread current = Thread.currentThread();
    //2.獲取同步狀態(tài)
    int c = getState();
    //3.state==0表示當前鎖處于空閑狀態(tài)
    if (c == 0) {
    //3.1 通過CAS獲取鎖,獲取成功后設置為當前線程所有
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //4.判斷鎖持有的線程是否為當前線程,可重入
    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;
}
  • #tryRelease(int releases)
protected final boolean tryRelease(int releases) {
    //1. 剩余可重入數量
    int c = getState() - releases;
    //2. 持有鎖的線程不是當前線程,直接拋出異常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //3. c == 0 表示已經釋放完全了,其他線程可以獲取同步狀態(tài)了
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}
  • 其他的方法
protected final boolean isHeldExclusively() {
    // While we must in general read state before owner,
    // we don't need to do so to check if current thread is owner
    return getExclusiveOwnerThread() == Thread.currentThread();
}

final ConditionObject newCondition() {
    return new ConditionObject();
}

// Methods relayed from outer class

final Thread getOwner() {
    return getState() == 0 ? null : getExclusiveOwnerThread();
}

final int getHoldCount() {
    return isHeldExclusively() ? getState() : 0;
}

final boolean isLocked() {
    return getState() != 0;
}
/**
 * 自定義序列化邏輯
 */
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    s.defaultReadObject();
    setState(0); // reset to unlocked state
}

Sync實現類

  • NonfairSync,非公平鎖實現類
final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}
  • FairSync,公平鎖實現類
final void lock() {
    acquire(1);
}

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;
}
  • 區(qū)別
    非公平鎖和公平鎖獲取同步狀態(tài)的過程唯一的區(qū)別就在于,公平鎖在獲取同步狀態(tài)時多了一個限制條件#hasQueuedPredecessors()方法,是否有前序節(jié)點,即自己不是首個等待獲取同步狀態(tài)的節(jié)點。代碼如下:
public final boolean hasQueuedPredecessors() {
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    //頭節(jié)點 != 尾節(jié)點
    //同步隊列第一個節(jié)點不為null
    //當前線程是同步隊列第一個節(jié)點
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

這里主要是判斷當前線程是否位于 CLH 同步隊列中的第一個。如果是則返回 true ,否則返回 false 。

ReentrantLock

  • 構造方法
/**
 * Creates an instance of {@code ReentrantLock}.
 * This is equivalent to using {@code ReentrantLock(false)}.
 */
public ReentrantLock() {
    sync = new NonfairSync();
}

/**
 * Creates an instance of {@code ReentrantLock} with the
 * given fairness policy.
 *
 * @param fair {@code true} if this lock should use a fair ordering policy
 */
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

默認是非公平鎖,但是可以通過構造方法設置。

  • lock、trylock、unlock

public void lock() {
    sync.lock();
}

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}

public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

public void unlock() {
    sync.release(1);
}

總結

ReentrantLock 與 synchronized 的區(qū)別

  • 與synchronized相比,ReentrantLock提供了更多,更加全面的功能,具備更強的擴展性。例如:時間鎖等候,可中斷鎖等候,鎖投票。
  • ReentrantLock還提供了條件Condition,對線程的等待、喚醒操作更加詳細和靈活,所以在多個條件變量和高度競爭鎖的地方,ReentrantLock 更加適合。
  • ReentrantLock提供了可輪詢的鎖請求。它會嘗試著去獲取鎖,如果成功則繼續(xù),否則可以等到下次運行時處理,而synchronized則一旦進入鎖請求要么成功要么阻塞,所以相比synchronized而言,ReentrantLock會不容易產生死鎖些。
  • ReentrantLock 支持更加靈活的同步代碼塊,但是使用 synchronized 時,只能在同一個 synchronized塊結構中獲取和釋放。注意,ReentrantLock 的鎖釋放一定要在 finally 中處理,否則可能會產生嚴重的后果。
  • ReentrantLock 支持中斷處理,且性能較 synchronized 會好些。
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容