Java Condition.await()收到signal后需等unlock()才唤醒的原因
核心结论
你观察到的运行结果完全符合Java Condition的设计规范,和Javadoc描述没有冲突,问题出在你对await()返回条件的理解有遗漏。
关键逻辑说明
Javadoc只提到signal()是唤醒等待线程的触发条件之一,但没有明确说透await()方法返回的硬性前置要求:
被
signal()唤醒的线程,必须重新获取到与当前Condition绑定的锁,才能真正从await()方法处返回,继续执行后续代码。调用
signal()本身不会释放锁,锁的持有权仍然保留在调用signal()的线程手中,直到该线程主动调用unlock()方法释放锁。
对应代码的执行流程拆解
- 线程1首先拿到
rLock,休眠1s后进入while循环调用cond.await():这一步确实原子性释放了锁,线程1进入Condition的专属等待队列休眠,不再参与锁调度。 - 之前因为抢不到锁阻塞的线程2此时成功获取锁,执行到
cond.signal():这一步的作用仅仅是把线程1从Condition等待队列移动到rLock的锁竞争队列,标记线程1具备后续抢锁的资格,但此时锁仍然被线程2持有,线程1根本没有机会拿到锁,自然无法从await()返回打印Step5。 - 线程2继续执行后续逻辑:休眠3s、修改
aBool的值,直到进入finally块调用rLock.unlock()释放锁,此时锁变为可用状态,在锁竞争队列等待的线程1才能拿到锁,从await()方法返回,执行Step5的打印逻辑。
你实际运行得到的输出完全符合上述执行逻辑:
(Step 1) Thread 1 locks and sleeps 1s (Step 2) Thread 2 also requires lock, blocked (Step 3) Thread 1 enters while loop (Step 4) Thread 2 gets lock after thread 1 cond wait, sends signal (Step 6) Thread 2 sleeps 3s and set aBool (Step 7) unlock (Step 5) Thread 1 got signal
常见认知误区
很多人会混淆「线程被标记为唤醒」和「线程立刻获得执行权」两个概念,实际上Condition的signal()/await()机制和内置锁的Object.notify()/wait()机制逻辑完全一致:调用notify()后,被唤醒的线程同样需要等持有synchronized锁的线程释放锁、自己重新拿到锁之后,才能从wait()方法返回,不存在调用唤醒方法后被唤醒线程立刻插队执行的情况。
如果要实现你预期的Step5在Step6前执行的效果,只需要在cond.signal()调用后立刻释放锁,等线程1执行完对应逻辑后,线程2再重新抢锁执行后续操作即可,但这种写法需要严格控制锁的持有粒度,避免出现逻辑错误。
内容的提问来源于stack exchange,提问作者gongliming7
相关产品推荐
相关产品推荐

