Java多线程中的等待与通知机制

前言
在多线程编程中,线程之间的通信是一个常见的需求。然而,由于线程调度的不可预测性,我们无法直接控制线程的执行顺序。因此,我们需要一种机制来协调线程之间的行为。Java 提供了 wait() 和 notify() 方法来实现线程间的等待与通知机制,本文将通过实例详细讲解其使用方法和原理。
问题引入
假设我们有两个线程,thread1 负责打印一条消息,而 thread2 负责生成这条消息。我们希望 thread1 在 thread2 生成消息后再打印,但直接运行以下代码可能会导致 thread1 打印出 null,因为线程调度的顺序无法保证:
java复制
public class SimpleWaitNotifyDemo {
private static String message;

public static void main(String[] args) {
    Thread thread1 = new Thread(() -> {
        System.out.println(message);
    });
    Thread thread2 = new Thread(() -> {
        message = "A message from thread2";
    });
    thread1.start();
    thread2.start();
}

}
使用 Guarded Blocks 的低效解决方案
为了确保 thread1 等待 thread2 完成消息的生成,我们可能会想到使用一个循环来不断检查消息是否为空:
java复制
public class SimpleWaitNotifyDemo {
private static String message;

public static void main(String[] args) {
    Thread thread1 = new Thread(() -> {
        while (message == null) {
            // 空循环,浪费 CPU 资源
        }
        System.out.println(message);
    });
    Thread thread2 = new Thread(() -> {
        message = "A message from thread2";
    });
    thread1.start();
    thread2.start();
}

}
然而,这种方法非常低效,因为它会一直占用 CPU 资源,不断检查条件是否满足。
使用 wait() 和 notify() 的高效解决方案
Java 提供了 wait() 和 notify() 方法来解决上述问题。wait() 方法会使当前线程进入等待状态,直到其他线程调用 notify() 或 notifyAll() 方法唤醒它。这些方法需要在同步块(synchronized)中调用,因为它们依赖于对象的监视器锁(intrinsic lock)。
示例代码
java复制
public class SimpleWaitNotifyDemo {
private static String message;

public static void main(String[] args) {
    Object lock = new Object(); // 锁对象
    Thread thread1 = new Thread(() -> {
        synchronized (lock) {
            while (message == null) {
                try {
                    lock.wait(); // 等待消息被设置
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println(message);
    });

    Thread thread2 = new Thread(() -> {
        synchronized (lock) {
            message = "A message from thread2";
            lock.notify(); // 唤醒等待的线程
        }
    });

    thread1.start();
    thread2.start();
}

}
在这个例子中,thread1 会在消息为空时调用 lock.wait() 进入等待状态,并释放锁。thread2 在设置消息后调用 lock.notify() 唤醒等待的线程。这种方式避免了空循环,提高了效率。
wait() 和 notify() 的特点
释放锁:wait() 方法会释放当前线程持有的锁,允许其他线程进入同步块。
异常处理:wait() 方法可能会抛出 InterruptedException,表示线程被中断。
锁的限制:wait() 和 notify() 必须在同步块中调用,否则会抛出 IllegalMonitorStateException。
notify() 与 notifyAll() 的区别
notify():唤醒一个等待该对象锁的线程。如果有多个线程等待,则由线程调度器决定唤醒哪一个。
notifyAll():唤醒所有等待该对象锁的线程。通常在多线程竞争资源时使用,确保所有等待线程都有机会重新竞争锁。
示例:使用 notifyAll() 的场景
假设我们有一个线程池,多个线程都在等待某个任务完成。使用 notifyAll() 可以唤醒所有等待的线程,让它们重新竞争锁并继续执行:
java复制
public class TaskPool {
private boolean taskCompleted = false;

public synchronized void completeTask() {
    taskCompleted = true;
    notifyAll(); // 唤醒所有等待的线程
}

public synchronized void waitForTask() throws InterruptedException {
    while (!taskCompleted) {
        wait(); // 等待任务完成
    }
    System.out.println("Task completed, proceeding...");
}

}
总结
wait() 和 notify() 是 Java 提供的用于线程间通信的强大工具。通过合理使用它们,我们可以实现高效的线程协调,避免线程之间的竞争和资源浪费。在实际开发中,我们需要注意以下几点:
确保 wait() 和 notify() 在同步块中调用。
使用 while 循环检查条件,而不是 if,因为线程可能会被虚假唤醒。
根据实际需求选择 notify() 或 notifyAll()。
希望本文的介绍和实例能够帮助你更好地理解和使用 Java 的线程等待与通知机制。

你可能感兴趣的:(java,python,前端,个人开发)