1. 程式人生 > >從使用角度看 ReentrantLock 和 Condition

從使用角度看 ReentrantLock 和 Condition

阻塞 transient string turn his 介紹 ner await dex

java 語言中談到鎖,少不了比較一番 synchronized 和 ReentrantLock 的原理,本文不作分析,只是簡單介紹一下 ReentrantLock 的用法,從使用中推測其內部的一些原理。

代碼示例:

public static void main(String[] args) throws InterruptedException {
        final ReentrantLock lock = new ReentrantLock();
        final Condition con1 = lock.newCondition();
        final
Condition con2 = lock.newCondition(); // lock.lock(); Thread t1 = new Thread(new Runnable() { @Override public void run() { try { lock.lock(); con1.await(); System.out.println(
"wake from cond1"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }); t1.start(); Thread t2 = new Thread(new Runnable() { @Override
public void run() { try { lock.lock(); con2.await(); System.out.println("wake from cond2"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }); t2.start(); Thread t3 = new Thread(new Runnable() { @Override public void run() { try { lock.lock(); con2.await(); System.out.println("wake from cond2"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }); t3.start(); Thread.sleep(100); try { lock.lock(); System.out.println("lock.getQueueLength() = " + lock.getQueueLength()); System.out.println("lock.getWaitQueueLength(con1) = " + lock.getWaitQueueLength(con1)); System.out.println("lock.getWaitQueueLength(con2) = " + lock.getWaitQueueLength(con2)); con1.signal(); con2.signalAll(); Thread.sleep(100); System.out.println("lock.getWaitQueueLength(con1) = " + lock.getWaitQueueLength(con1)); System.out.println("lock.getWaitQueueLength(con2) = " + lock.getWaitQueueLength(con2)); } finally { lock.unlock(); } }

以 ReentrantLock.getQueueLength 和 ReentrantLock.getWaitQueueLength 作為入口:前者作用是返回獲取鎖失敗,處於等待狀態的線程個數,後者是返回等待某一個 condition 的線程個數。

// int java.util.concurrent.locks.ReentrantLock.getQueueLength()
public final int getQueueLength() {
    return sync.getQueueLength();
}

// int java.util.concurrent.locks.AbstractQueuedSynchronizer.getQueueLength()
public final int getQueueLength() {
    int n = 0;
    for (Node p = tail; p != null; p = p.prev) {
        if (p.thread != null)
            ++n;
    }
    return n;
}

很簡單,就是遍歷阻塞線程的鏈表。

// int java.util.concurrent.locks.ReentrantLock.getWaitQueueLength(Condition condition)
public int getWaitQueueLength(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
        throw new IllegalArgumentException("not owner");
    return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
}

// int java.util.concurrent.locks.AbstractQueuedSynchronizer.getWaitQueueLength(ConditionObject condition)
public final int getWaitQueueLength(ConditionObject condition) {
    if (!owns(condition))
        throw new IllegalArgumentException("Not owner");
    return condition.getWaitQueueLength();
}

// int java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject.getWaitQueueLength()
protected final int getWaitQueueLength() {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    int n = 0;
    for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
        if (w.waitStatus == Node.CONDITION)
            ++n;
    }
    return n;
}

同樣,也是遍歷鏈表,不同的是,這是 Condition 的鏈表。

現在,我們知道,ReentrantLock 中有 2 種不同的鏈表,其一是阻塞線程,其二是 Condition 等待鏈表,2 中鏈表都是使用 Node:

// java.util.concurrent.locks.AbstractQueuedSynchronizer.Node 
static final class Node {
    static final Node EXCLUSIVE = null;
    // 線程阻塞節點的狀態
    static final int CANCELLED =  1;
    static final int SIGNAL    = -1;
    // condition 節點的狀態
    static final int CONDITION = -2;
    static final int PROPAGATE = -3;
    volatile int waitStatus;
    volatile Node prev;
    volatile Node next;
    volatile Thread thread;
}

節點的類型表明鏈表的類型。

看看 Condition 實現類的 api:

public class ConditionObject implements Condition, java.io.Serializable {
    private static final long serialVersionUID = 1173984872572414699L;
    private transient Node firstWaiter;
    private transient Node lastWaiter;

    /**
     * Adds a new waiter to wait queue.
     * @return its new wait node
     */
    private Node addConditionWaiter() {
        Node t = lastWaiter;
        // If lastWaiter is cancelled, clean out.
        if (t != null && t.waitStatus != Node.CONDITION) {
            unlinkCancelledWaiters();
            t = lastWaiter;
        }
        Node node = new Node(Thread.currentThread(), Node.CONDITION);
        if (t == null)
            firstWaiter = node;
        else
            t.nextWaiter = node;
        lastWaiter = node;
        return node;
    }

    public final void await() throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        Node node = addConditionWaiter();
        int savedState = fullyRelease(node);
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) {
            LockSupport.park(this);
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
        }
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null) // clean up if cancelled
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
    }

    /**
     * Moves the longest-waiting thread, if one exists, from the
     * wait queue for this condition to the wait queue for the
     * owning lock.
     *
     * @throws IllegalMonitorStateException if {@link #isHeldExclusively}
     *         returns {@code false}
     */
    public final void signal() {
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        Node first = firstWaiter;
        if (first != null)
            doSignal(first);
    }
}

在開頭的示例中,創建了 2 個 Condition 對象,每個Condition 對象有一個自己的等待鏈表。

從使用角度看 ReentrantLock 和 Condition