任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object
上),主要包括wait()
、wait(long timeout)
、notify()
以及notifyAll()
方法,这些方法与synchronized同步关键字配合,以实现等待/通知模式。
Condition接口也提供了类似Object的监视器方法,与Lock配合可以实等待/通知模式,但是这两者在使用方式以及功能特性上还是有差别的。
对比:
对比项 | Object Monitor Methods | Condition |
---|---|---|
前置条件 | 获取对象锁 | 调用Lock.lock() 调用 Lock.newCondition() 获取Condition对象 |
调用方法 | 直接调用 | 直接调用 |
等待队列个数 | 一个 | 多个 |
当前线程释放锁进入等待状态 | 支持 | 支持 |
当前线程释放锁进入等待状态,在等待中不响应中断 | 支持 | 不支持 |
进入超时等待 | 支持 | 支持 |
进入等待状态到某个时间 | 不支持 | 支持 |
唤醒等待队列中的一个线程 | 支持 | 支持 |
唤醒全部线程 | 支持 | 支持 |
直接上示例代码:
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void conditionWait() throws InterruptedException {
lock.lock();
try{
condition.await();
}finally {
lock.unlock();
}
}
如示例所示,一般都会将Condition对象作为成员变量。当调用await()
方法后,当前线程会释放锁并在此等待,而其他线程调用Condition对象的signal()
方法,通知当前线程后,当前线程才从await()
方法返回,并且在返回前已经获取了锁。
Condition方法描述:
// 线程进入等待状态,直到被中断或者signal()
void await() throws InterruptedException;
// 进入等待状态,可以看出对中断不敏感
void awaitUninterruptibly();
// 进入等待直到 被中断、signal()、超时
long awaitNanos(long nanosTimeout) throws InterruptedException;
// 与上面相同
boolean await(long time, TimeUnit unit) throws InterruptedException;
// 进入等待直到 被中断、signal()、到某个时间点
boolean awaitUntil(Date deadline) throws InterruptedException;
// 唤醒线程
void signal();
void signalAll();
ConditionObject是同步器AbstractQueuedSynchronizer的内部类,可以理解为在同步队列的基础上加了一个等待队列!
等待队列结构如上图所示
注意:述节点引用更新的过程并没有使用CAS保证,原因在于调用
await()
方法的线程必定是获取了锁的线程,也就是说该过程是由锁来保证线程安全的。
我们下面通过一张图来看看Lock怎么拥有:同步队列和等待队列
Condition的实现是同步器的内部类,因此每个Condition实例都能够访问同步器提供的方法,相当于每个Condition都拥有所属同步器的引用
调用Condition的await()
方法,会使当前线程进入等待队列并释放锁,同时线程状态变为等待状态。当从await()
方法返回时,当前线程一定获取了Condition相关联的锁。
下面看一下AQS下ConditionObject类中的await方法:
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);
}
当前线程加入Condition的等待队列的过程如下图:
调用Condition的signal()
方法,唤醒等待时间最长的节点,由上图可以看出等待时间最长的节点是首节点。
下面给出 signal()
的源代码:
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
// 内部会使用LockSupport.unpark()来唤醒线程竞争同步状态
doSignal(first);
}
signal()的过程就是将线程从waiting状态->blocked状态的过程
Condition的signalAll()
方法,相当于对等待队列中的每个节点均执行一次signal()
方法,效果就是将等待队列中所有节点全部移动到同步队列中,并唤醒每个节点的线程。
LockSupport定义了一组的公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能。
下面从源代码分析一下:
// 阻塞当前线程
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
// 阻塞当前线程 不超过 nanos时间
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, nanos);
setBlocker(t, null);
}
}
// 阻塞线程至指定时间
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
// 唤醒线程
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}