Java 如何退出阻塞
在 Java 编程中,有时候我们会遇到需要在某些条件满足之后才能继续执行的情况,这就需要我们将线程阻塞起来,并在合适的时机唤醒线程,使其继续执行。本文将介绍几种常见的 Java 退出阻塞的方法,包括使用 wait()、notify()、notifyAll() 方法、使用 Lock 和 Condition 接口、以及使用 Future 和 CompletableFuture。
使用 wait()、notify()、notifyAll() 方法
Java 中的 wait()、notify() 和 notifyAll() 方法是 Object 类的方法,用于线程之间的通信。当一个线程调用 wait() 方法时,它会将当前线程放入等待队列中,然后释放持有的锁,进入等待状态。当另一个线程调用 notify() 或 notifyAll() 方法时,它会唤醒一个或所有等待的线程,使其继续执行。
下面是一个简单的示例,演示了如何使用 wait()、notify() 和 notifyAll() 方法实现线程的阻塞和唤醒:
class MyThread implements Runnable {
    private final Object lock;
    public MyThread(Object lock) {
        this.lock = lock;
    }
    public void run() {
        synchronized (lock) {
            try {
                System.out.println(Thread.currentThread().getName() + "开始执行");
                lock.wait();
                System.out.println(Thread.currentThread().getName() + "被唤醒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Main {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        Thread threadA = new Thread(new MyThread(lock), "ThreadA");
        Thread threadB = new Thread(new MyThread(lock), "ThreadB");
        threadA.start();
        threadB.start();
        Thread.sleep(1000);
        synchronized (lock) {
            lock.notify(); // 唤醒一个等待的线程
            // lock.notifyAll(); // 唤醒所有等待的线程
        }
    }
}
上述代码中,我们创建了两个线程 ThreadA 和 ThreadB,它们共享同一个 lock 对象。在 MyThread 类的 run() 方法中,我们使用 lock.wait() 将线程阻塞起来。在 main() 方法中,我们调用 lock.notify() 方法唤醒一个等待的线程。
当代码运行时,ThreadA 和 ThreadB 会先执行到 lock.wait() 处,然后被阻塞住。在 main() 方法中的 lock.notify() 方法被执行后,其中一个线程会被唤醒,继续执行。输出结果可能是以下两种情况之一:
- ThreadA开始执行 ThreadB开始执行 ThreadA被唤醒
- ThreadB开始执行 ThreadA开始执行 ThreadB被唤醒
状态图
下面是上述示例中线程的状态图:
stateDiagram
    [*] --> Runnable
    Runnable --> Blocked : 调用wait()方法
    Blocked --> Runnable : 调用notify()或notifyAll()方法
    Blocked --> [*] : 线程被中断
    Runnable --> Terminated : run() 方法执行完毕
使用 Lock 和 Condition 接口
Java 中的 Lock 接口和 Condition 接口提供了比 synchronized 关键字更加灵活和强大的线程同步机制。Lock 接口提供了与 synchronized 类似的互斥性,而 Condition 接口则提供了与 wait()、notify() 和 notifyAll() 方法类似的等待和通知机制。
下面是一个使用 Lock 和 Condition 接口的示例:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class MyThread implements Runnable {
    private final Lock lock;
    private final Condition condition;
    public MyThread(Lock lock, Condition condition) {
        this.lock = lock;
        this.condition = condition;
    }
    public void run() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread