对!while (!isDone) { wait(); } 这个循环的核心作用就是防止“虚假唤醒”,这是多线程编程中一个非常重要的细节。
什么是“虚假唤醒”?
Java 中,线程调用 wait() 后可能会在没有被其他线程调用 notify()/notifyAll() 的情况下被唤醒,这种现象就是“虚假唤醒”(spurious wakeup)。
它不是程序错误导致的,而是操作系统或 JVM 内部的调度机制引起的(比如线程调度的“惊群效应”,或某些底层优化)。简单说:wait() 可能无缘无故地醒过来。
为什么需要 while 循环来防虚假唤醒?
假设你把代码写成 if (!isDone) { wait(); }(用 if 而不是 while),会出现什么问题?
// 错误示例:用if判断,可能被虚假唤醒
public synchronized V get() throws Exception {if (!isDone) { wait(); // 若此处被虚假唤醒,直接跳出if}return result;
}
- 正常情况:线程A执行完任务,调用
notifyAll(),主线程被唤醒,isDone为true,返回结果——没问题。 - 虚假唤醒情况:主线程在
wait()时被无故唤醒,但此时isDone仍然为false(任务根本没完成)。由于用了if,只会判断一次,唤醒后直接跳出if,返回result(可能是null或错误值),导致逻辑错误。
while 循环如何解决问题?
用 while (!isDone) { wait(); } 时,每次线程从 wait() 中醒来(无论正常唤醒还是虚假唤醒),都会重新检查 isDone 的值:
- 如果是正常唤醒:
isDone已经被设为true,循环条件不成立,跳出循环,返回正确结果。 - 如果是虚假唤醒:
isDone仍然为false,循环条件成立,会再次执行wait()继续阻塞,直到任务真的完成并被正常唤醒。
这就像你在等外卖时,每次被“敲门声”吵醒(无论真有人敲门还是听错了),都会先通过猫眼确认是不是外卖员(检查 isDone),不是的话就继续睡——确保不会白跑一趟。
结合你的代码,总结 while 循环的必要性
- 你的
MyFutureTask中,get()方法的核心逻辑是“直到任务完成(isDone=true),才返回结果”。 - 虚假唤醒会破坏这个逻辑(让线程在任务未完成时就继续执行)。
while循环通过“唤醒后重新检查条件”,确保只有当isDone真的为true时,才会退出等待——完美解决虚假唤醒问题。
这也是 Java 官方文档中明确推荐的写法:永远在 while 循环中使用 wait(),而不是 if,目的就是防止虚假唤醒。