Condition 接口与 Lock 配合实现了等待 / 通知模式,这个和 Object 的监视器方法(wait、notify、notifyAll 等方法)一样,都是实现了等待 / 通知模式,但这两者在使用的方式以及功能特性上还是有差别的。
对比项 | Object Monitor Methods | Condition |
前置条件 | 获取对象的锁 |
调用Lock.lock()获取锁 调用Lock.newCondition()获取Condition对象 |
调用方式 | 直接调用,如:Object.wait() | 直接调用,如:condition.await() |
等待队列个数 | 一个 | 多个 |
当前线程释放锁并进入等待状态 | 支持 | 支持 |
当前线程释放锁并进入等待状态, 在等待状态中不响应中断 |
不支持 | 支持 |
当前线程释放锁并进入等待超时状态 | 支持 | 支持 |
当前现场释放锁并进入等待状态到将来某个时间 | 不支持 | 支持 |
唤醒等待队列中的一个线程 | 支持 | 支持 |
唤醒等待队列中的所有线程 | 支持 | 支持 |
在 Object 监视模型中,一个对象有一个同步队列和一个等待队列,而 AQS 拥有一个同步队列和多个等待队列。
Object 的监视模型:
Condition 的同步队列与等待队列:
Condition 部分方法以及描述:
方法名称 | 描述 |
void await() throws
|
当前线程进入等待状态,直到被通知(signal)或中断, 当前线程才会进入运行状态且从此await方法返回(表明 该线程已经获取了Condition对象对应的锁),包括: 其他线程(调用了interrupt()方法中断了当前线程)调用 该 Condition 的 signal() 或 signalAll()方法,而当前线 程被选中唤醒 |
void awaitUninterruptibly()
|
当前线程进入等待状态,知道被通知,对中断不敏感 |
long awaitNanos(long nanosTimeout) |
当前线程进入等待状态直到被通知、中断或者是超时, 返回值代表剩余的时间,如果在nanosTimeout纳秒 之前被唤醒,返回值是(nanosTimeout - 实际耗时)。 若返回的是0或者负数,超时了。 |
boolean awaitUntil(Date deadline)
|
当前线程进入等待状态直到被通知、中断或者是到某个 时间。没有到指定时间就被通知,返回true;否则, 返回false |
void signal()
|
唤醒一个等待在Condition上的线程,该线程从等待方法 返回前必须获得与Condition相关的锁 |
void signalAll()
|
唤醒所有等待在Condition上的线程,能够从等待方法 返回的线程必须获得与Condition相关联的锁 |
Condition 的实现
① 等待队列
前面的 Condition 的同步队列与等待队列,队列中的每个节点都包含一个线程引用,该线程就是在Condition对象上等待的线程,如果一个线程调用Condition.await()方法,那么该线程将会释放锁、构造成节点加入等待队列进入等待状态。同步队列和等待队列的节点类型都是AQS中的静态内部类Node。Condition是AQS中的静态内部类。
② 等待
调用Condition的await方法,会使当前线程进入等待队列并释放锁,同时线程状态变成等待状态。当从await方法返回时,当前线程一定获取了Condition相关联的锁。
1 public final void await() throws InterruptedException { 2 if (Thread.interrupted()) 3 throw new InterruptedException(); 4 Node node = addConditionWaiter(); // 当前线程加入等待队列 5 int savedState = fullyRelease(node); // 释放同步状态,也就是释放锁 6 int interruptMode = 0; 7 while (!isOnSyncQueue(node)) { 8 LockSupport.park(this); 9 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) 10 break; 11 } 12 if (acquireQueued(node, savedState) && interruptMode != THROW_IE) 13 interruptMode = REINTERRUPT; 14 if (node.nextWaiter != null) // 线程被取消的话要清除节点 15 unlinkCancelledWaiters(); 16 if (interruptMode != 0) 17 reportInterruptAfterWait(interruptMode); 18 }
如果队列的角度来看await()方法,当调用await()方法时,相当于把同步队列的首节点(获取了锁的节点)移动到Condition的等待队列中,实际上是通过addConditionWaiter()方法把当前线程构造成一个新的节点并将其加入等待队列中。
③ 通知
调用该方法的前置条件是要获取了锁,方法首先用isHeldExclusively()检查,接着获取等待队列的首节点,将其移动到同步队列并使用LockSupport工具类唤醒节点中的线程。
1 public final void signal() { 2 if (!isHeldExclusively()) 3 throw new IllegalMonitorStateException(); 4 Node first = firstWaiter; 5 if (first != null) 6 doSignal(first); 7 }
1 private void doSignal(Node first) { 2 do { 3 if ( (firstWaiter = first.nextWaiter) == null) 4 lastWaiter = null; 5 first.nextWaiter = null; 6 } while (!transferForSignal(first) && 7 (first = firstWaiter) != null); 8 }
1 final boolean transferForSignal(Node node) { 2 /* 3 * 如果无法更改waitStatus,则该节点会被取消 4 */ 5 if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) 6 return false; 7 8 /* 9 * 拼接到队列上并尝试设置前任的waitStatus以指示线程(可能)正在等待。 10 * 如果取消或尝试设置waitStatus失败,请唤醒以重新同步 11 * (在这种情况下,waitStatus可能是暂时性且无害的错误) 12 */ 13 Node p = enq(node); 14 int ws = p.waitStatus; 15 if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) 16 LockSupport.unpark(node.thread); 17 return true; 18 }
节点从等待队列移动到同步队列: