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