在 Java 中,wait()
方法通常与 synchronized
关键字一起使用,以确保线程之间的正确同步。wait()
方法使当前线程进入等待状态,直到其他线程调用 notify()
或 notifyAll()
方法来唤醒它。
在 Java 并发编程中,wait()
方法通常放在 while
循环中而不是 if
语句中,这种做法主要是为了解决下面的问题:
防止虚假唤醒
确保线程被唤醒时满足条件,避免因条件变化导致线程继续执行错误的操作
什么是虚假唤醒(spurious wakeups)
虚假唤醒是指线程在没有被显式唤醒(例如通过 notify()
或 notifyAll()
)的情况下,仍然从 wait()
状态中恢复执行的现象。
这种情况可能因为操作系统的线程调度或其他线程的干扰而发生。如果 wait()
方法放在 if
语句中,一旦线程被虚假唤醒,它可能会在没有获得锁的情况下退出循环,从而导致程序逻辑错误。
使用 while 循环的原因
防止虚假唤醒
Java 中的 wait()
方法可能会出现“虚假唤醒”现象,即线程在没有被其他线程调用 notify()
或 notifyAll()
的情况下被唤醒,如果不在循环中检查等待条件,程序就会在没有满足结束条件的情况下退出。
为了避免这种情况,可以将 wait()
放在 while
循环中,每次从 wait()
方法返回后,都需要重新检查条件是否满足,这样可以确保线程在等待条件未满足前,会持续等待。
即使线程因为虚假唤醒而提前醒来,循环会继续检查条件,如果条件不满足,线程会再次调用 wait()
方法,从而避免虚假唤醒对程序逻辑的影响。
使用 while
循环可以确保在每次从 wait()
方法返回后,都会重新检查条件是否满足,从而避免因虚假唤醒而导致的错误。以下是一个典型的使用场景:
synchronized (monitor) { while (!condition) { try { monitor.wait(); } catch (InterruptedException e) { // 处理中断异常 } } // 条件满足,继续执行 }
在这个例子中,monitor
是一个对象,condition
是一个布尔条件。
线程在进入 synchronized
块后,会检查 condition
是否为 true
。如果条件不满足,线程会调用 monitor.wait()
进入等待状态,并释放锁。
当其他线程调用 monitor.notify()
或 monitor.notifyAll()
时,等待的线程会被唤醒,并重新进入 synchronized
块。此时,线程会再次检查 condition
,只有在条件满足的情况下才会继续执行。
确保条件满足
当线程被唤醒时,它需要重新检查等待的条件是否仍然满足。如果条件不满足,线程应该再次进入等待状态。
通过在 while
循环中调用 wait()
,可以确保线程在每次唤醒后都重新检查条件,从而避免因条件变化而导致的错误行为。
避免条件变化
在多线程环境中,其他线程可能会在当前线程进入等待状态后修改条件。如果 wait()
方法放在 if
语句中,当线程被唤醒时,可能无法及时检测到条件的变化,从而导致线程继续执行错误的操作。