本文共 2999 字,大约阅读时间需要 9 分钟。
在多线程编程中,虚假唤醒是一个常见的问题。它通常发生在多个线程等待同一条件满足时。如果条件在某个时间点被触发,所有处于等待状态的线程都会被唤醒。然而,只有其中一个线程能够获取所需的锁或资源进行操作,其余线程虽然被唤醒,但由于无法继续执行会陷入死循环或错误的处理流程。这种情况下,唤醒的线程没有实际意义,导致资源浪费和逻辑错误。
在多线程环境中,判断线程是否需要阻塞(调用 wait())时,使用 if 判断会导致虚假唤醒。例如,当 product 小于等于 1 时,所有等待 product >= 1 的线程都会被唤醒。然而,只有一个线程能够获取锁并执行 wait(),其余未能获取锁的线程会被唤醒后继续执行,这样会导致这些线程在没有必要的情况下进行操作,引发逻辑错误。
if (product >= 1) { wait();} 上述代码会导致虚假唤醒。解决方法是将 if 替换为 while,确保所有等待的线程只有在条件满足时才被唤醒,并且只有一个线程能够继续执行。
while (product >= 1) { wait();} if 会产生虚假唤醒if 语句只会在条件满足时执行一次 wait(),然后立即继续执行后续代码。然而,如果在 if 语句执行后条件又再次满足,其他线程仍然会被唤醒并继续等待,但由于 if 语句已经执行过,后续的条件检查不会再次进行。这种情况下,其他线程虽然被唤醒,但由于条件不再满足,会陷入死循环或错误的处理逻辑。
将 while (product >= 1) 替换为 if (product >= 1) 就会产生虚假唤醒。if 语句只会在条件满足时唤醒所有等待的线程,但只允许一个线程获取锁并继续执行,其余线程虽然被唤醒,但由于条件不再满足,会继续等待或执行无效操作。
以下是一个典型的生产者消费者场景,展示虚假唤醒的影响:
public class Test { public static void main(String[] args) { Clerk clerk = new Clerk(); Producer producer = new Producer(clerk); Customer customer = new Customer(clerk); new Thread(producer, "生产者A").start(); new Thread(customer, "消费者A").start(); new Thread(producer, "生产者B").start(); new Thread(customer, "消费者B").start(); }}class Clerk { private int product = 0; public synchronized void add() { while (product >= 1) { System.out.println(Thread.currentThread().getName() + "---还有货物:" + product); try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } product += 1; System.out.println(Thread.currentThread().getName() + " 补货完成,剩余货物:" + product); notifyAll(); } public synchronized void sale() { while (product <= 0) { System.out.println(Thread.currentThread().getName() + " 缺货中:" + product); try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + " 已补货,开始消费..."); product -= 1; notifyAll(); }}class Producer implements Runnable { private Clerk clerk; public Producer(Clerk clerk) { this.clerk = clerk; } @Override public void run() { for (int i = 0; i < 20; i++) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } clerk.add(); } }}class Customer implements Runnable { private Clerk clerk; public Customer(Clerk clerk) { this.clerk = clerk; } @Override public void run() { for (int i = 0; i < 20; i++) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } clerk.sale(); } }} 在上述代码中,如果将 while 替换为 if,就会出现虚假唤醒的情况。例如,当 product 小于等于 1 时,所有等待的线程都会被唤醒,但只有一个线程能够获取锁并继续执行,其余线程虽然被唤醒,但由于条件不再满足,会继续等待或执行无效操作。
转载地址:http://lofh.baihongyu.com/