从使用角度看 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)