从使用角度看 ReentrantLock 和 Condition

Posted 偶尔发呆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从使用角度看 ReentrantLock 和 Condition相关的知识,希望对你有一定的参考价值。

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的主要内容,如果未能解决你的问题,请参考以下文章

面试官:从源码角度讲讲ReentrantLock及队列同步器(AQS)

面试官:从源码角度讲讲ReentrantLock及队列同步器(AQS)

从ReentrantLock的实现看AQS的原理及应用

Java多线程12:ReentrantLock中的方法

从ReentrantLock的实现看AQS的原理及应用

(十一)synchronized和ReentrantLock使用场景(2)(未完待续)