Java唤醒阻塞线程的多种方法(附带示例)
在 Java 中,当线程由于调用了某些阻塞操作(如 wait()、join()、sleep() 方法)或因等待同步资源而被阻塞时,可以使用特定的方法来唤醒它。
下面我们详细介绍几种唤醒阻塞线程的方法和实现示例。
注意,signal() 或 signalAll() 在使用时必须用 lock() 和 unlock() 方法包裹,否则会在运行时抛出 IIlegalMonitorStateException 异常。
使用 LockSupport 的 park() 和 unpark(thread) 方法可以阻塞和唤醒线程,而不需要线程拥有某个对象的锁,这使得它比 Object 的 wait() 和 notify() 方法更加灵活。
上面介绍的每种方法都有各自的特点,适用于不同的场景,所以在实际应用中需要根据具体的阻塞情况来选择合适的方法。
下面我们详细介绍几种唤醒阻塞线程的方法和实现示例。
1) notify()或notifyAll()方法
当线程调用了对象的 wait() 方法后进入等待状态时,可以使用相同对象上的 notify() 或 notifyAll() 方法来唤醒线程。
public class WaitNotifyExample {
final static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread waitingThread = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("Thread is going to wait.");
lock.wait();
System.out.println("Thread is woken up.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
waitingThread.start();
Thread.sleep(1000); // 确保线程开始等待
synchronized (lock) {
lock.notify(); // 唤醒lock对象上等待的单个线程
// lock.notifyAll(); // 唤醒lock对象上所有等待的线程
}
}
}
notify()方法用于唤醒lock对象上等待的单个线程。如果有多个线程在等待,则只有其中的一个线程能被唤醒,具体唤醒哪一个线程由虚拟机决定。使用notifyAll()方法可以唤醒所有等待的线程。2) 使用Condition的signal()或signalAll()方法
当线程在某个条件设置等待时可以使用 Condition 的 await() 方法,然后可以使用 Condition 的 signal() 或 signalAll() 方法来唤醒线程。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockConditionExample {
private static final Lock lock = new ReentrantLock();
private static final Condition condition = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
Thread waitingThread = new Thread(() -> {
lock.lock();
try {
System.out.println("Thread is going to await.");
condition.await(); // 等待在Condition上
System.out.println("Thread is woken up.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
});
waitingThread.start();
Thread.sleep(1000); // 确保线程开始等待
lock.lock();
try {
condition.signal(); // 唤醒一个等待在Condition上的线程
// condition.signalAll(); // 唤醒所有等待在Condition上的线程
} finally {
lock.unlock();
}
}
}
如果使用 JUC 中 Condition 的 await() 方法阻塞线程,可以使用 signal() 或 singnalAll() 方法来唤醒线程。需要使用 lock 对象的 newCondition() 方法获得 Condition 对象(可获得多个),Condition 实现了公平锁,默认是非公平锁。注意,signal() 或 signalAll() 在使用时必须用 lock() 和 unlock() 方法包裹,否则会在运行时抛出 IIlegalMonitorStateException 异常。
3) 使用interrupt()方法
对于因调用了 sleep()、join() 而处于阻塞状态的线程,可以使用 interrupt() 方法来中断线程,这会导致线程抛出 InterruptedException 并返回运行状态。
public class InterruptExample {
public static void main(String[] args) throws InterruptedException {
Thread sleepingThread = new Thread(() -> {
try {
System.out.println("Thread is going to sleep.");
Thread.sleep(10000); // 线程将会睡眠10s
} catch (InterruptedException e) {
System.out.println("Thread is interrupted while sleeping.");
Thread.currentThread().interrupt();
}
});
sleepingThread.start();
Thread.sleep(1000); // 确保线程开始睡眠
sleepingThread.interrupt(); // 中断睡眠线程,导致线程抛出 InterruptedException并返回运行状态
}
}
4) 利用Semaphore类
Semaphore 可以控制同时访问某个资源的线程数。线程可以通过 acquire() 方法获取许可,如果没有就等待,而 release() 方法用于释放许可。如果线程因为等待获取 Semaphore 上的许可而被阻塞,可以通过释放一个许可来唤醒它。
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
private static final Semaphore semaphore = new Semaphore(0);
public static void main(String[] args) {
Thread waitingThread = new Thread(() -> {
try {
System.out.println("Thread is going to acquire.");
semaphore.acquire(); // 线程会等待获取许可
System.out.println("Thread got the permit.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
waitingThread.start();
System.out.println("Main thread is going to release a permit.");
semaphore.release(); // 释放许可
}
}
5) 利用LockSupport类
LockSupport 是 JUC 中的一个工具类,它提供了基本的线程同步功能。使用 LockSupport 的 park() 和 unpark(thread) 方法可以阻塞和唤醒线程,而不需要线程拥有某个对象的锁,这使得它比 Object 的 wait() 和 notify() 方法更加灵活。
public class LockSupportExample {
public static void main(String[] args) throws InterruptedException {
final Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ":被阻塞");
LockSupport.park(); // 阻塞当前线程
if (Thread.interrupted()) { // 检查中断标志
System.out.println(Thread.currentThread().getName() + ":被中断");
} else {
System.out.println(Thread.currentThread().getName() + ":被唤醒");
}
});
thread.start();
Thread.sleep(2000); // 确保线程已经启动并且被阻塞
System.out.println(Thread.currentThread().getName() + ":准备唤醒线程");
LockSupport.unpark(thread); // 唤醒被阻塞的线程
}
}
在上述的代码中,子线程会通过 LockSupport 的 park() 方法阻塞自己。主线程在等待 2s 后,通过调用 LockSupport 的 unpark(thread) 方法来唤醒被阻塞的线程。上面介绍的每种方法都有各自的特点,适用于不同的场景,所以在实际应用中需要根据具体的阻塞情况来选择合适的方法。
ICP备案:
公安联网备案: