Java中暂停执行主要通过Thread.sleep()、LockSupport.park()及等待/通知机制实现,Thread.sleep()是最常用方式,让当前线程暂停指定毫秒数,需处理InterruptedException中断异常;LockSupport.park()可精确控制线程暂停,常用于并发工具;等待/通知机制(wait/notify/notifyAll)需在同步块中使用,避免IllegalMonitorStateException,需注意异常处理、中断响应,避免长时间阻塞影响性能,合理选择暂停方式以适配不同场景。
Java中的暂停设置:实现程序可控停顿的多种方式
在Java程序开发中,"暂停"是一个常见需求——无论是模拟延迟、控制执行节奏,还是实现任务调度中的停顿机制,合理的暂停设置都能让程序行为更加可控,本文将系统介绍Java中实现暂停的核心方法、适用场景及注意事项,帮助开发者根据需求选择最合适的方案。
基础线程休眠:Thread.sleep()
Thread.sleep()是Java中最基础、最常用的暂停方式,它让当前执行的线程进入阻塞状态,指定时间后再恢复执行。
原理与用法
Thread.sleep(long millis)会使当前线程暂停至少millis毫秒(底层受系统调度精度影响,可能存在微小误差),期间线程不会获取CPU时间片,直到休眠结束或被中断,该方法会抛出InterruptedException,需显式处理(通常是捕获或向上抛出)。
public class SleepDemo {
public static void main(String[] args) {
System.out.println("开始休眠:" + System.currentTimeMillis());
try {
// 暂停2秒
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("休眠被中断:" + e.getMessage());
Thread.currentThread().interrupt(); // 恢复中断状态
}
System.out.println("休眠结束:" + System.currentTimeMillis());
}
}
适用场景
- 简单延迟:如模拟网络请求延迟、控制任务执行间隔
- 限流控制:通过固定间隔暂停,避免高频调用
- 动画效果:在GUI应用中控制帧率更新
注意事项
- 异常处理:
sleep()会响应中断(其他线程调用该线程的interrupt()方法),捕获异常后需恢复中断状态(Thread.currentThread().interrupt()),否则会中断后续的中断检测逻辑 - 精度限制:休眠时间受系统时钟精度影响,现代Java版本通常可达到毫秒级精度(Windows系统约1-15ms,Linux系统通常更高)
- 线程阻塞:休眠期间线程不会释放已获取的锁(如
synchronized锁),可能导致其他线程阻塞 - 系统负载:在高负载系统上,实际休眠时间可能略长于指定时间
底层暂停机制:LockSupport.park()
LockSupport是Java并发包中的工具类,提供park()和unpark()方法,用于实现线程的暂停与恢复,它是AQS(AbstractQueuedSynchronizer)等并发框架的基础组件。
原理与用法
LockSupport.park()会让当前线程暂停,直到其他线程调用LockSupport.unpark(Thread thread)唤醒该线程,或线程被中断,与sleep()不同,park()不会抛出InterruptedException,需通过Thread.interrupted()或Thread.isInterrupted()检测中断状态。
import java.util.concurrent.locks.LockSupport;
public class ParkDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("线程暂停:" + System.currentTimeMillis());
LockSupport.park(); // 暂停当前线程
System.out.println("线程恢复:" + System.currentTimeMillis());
});
thread.start();
try {
Thread.sleep(1000); // 主线程等待1秒后唤醒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程唤醒子线程");
LockSupport.unpark(thread); // 唤醒暂停的线程
}
}
适用场景
- 精确控制:需要手动唤醒线程的场景(如线程池任务调度)
- 并发框架:
AQS、ReentrantLock等底层实现,用于线程阻塞/唤醒 - 响应中断:需自行处理中断状态,避免异常开销
- 高性能场景:比
wait()/notify()有更好的性能表现
注意事项
- 无中断异常:
park()不会抛出InterruptedException,需主动检查中断状态 - 许可机制:
unpark()可在park()之前调用,此时线程不会阻塞(相当于提前发放"许可") - 底层依赖:基于
Unsafe类实现,需谨慎使用,避免内存安全问题 - 唤醒顺序:多个线程被
park()时,unpark()的唤醒顺序是FIFO(先进先出)
条件等待:Condition.await()
在锁机制中,Condition接口提供了await()方法,允许线程在释放锁的前提下进入等待状态,直到其他线程调用signal()或signalAll()唤醒。
原理与用法
Condition.await()必须在Lock锁的保护下调用,它会释放当前锁并进入等待队列,直到被唤醒或中断,重新获取锁后才能继续执行,支持超时等待(await(long time, TimeUnit unit))和不响应中断的等待(awaitUninterruptibly())。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionDemo {
private static final Lock lock = new ReentrantLock();
private static final Condition condition = lock.newCondition();
public static void main(String[] args) {
new Thread(() -> {
lock.lock();
try {
System.out.println("线程等待:" + System.currentTimeMillis());
condition.await(); // 释放锁并等待
System.out.println("线程被唤醒:" + System.currentTimeMillis());
} catch (InterruptedException e) {
System.out.println("等待被中断:" + e.getMessage());
} finally {
lock.unlock();
}
}).start();
try {
Thread.sleep(1000); // 主线程等待1秒后唤醒
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
try {
System.out.println("主线程唤醒等待线程");
condition.signal(); // 唤醒等待线程
} finally {
lock.unlock();
}
}
}
适用场景
- 生产者-消费者模型:消费者等待生产者生成数据,生产者生成后唤醒消费者
- 精确同步:需在锁机制下控制线程暂停/唤醒,避免竞态条件
- 超时等待:需要设置最大等待时间的场景
- 多条件等待:使用多个
Condition实现不同的等待条件
注意事项
- 锁释放:
await()会自动释放锁,但必须在锁的保护下调用 - 唤醒顺序:
signal()随机唤醒一个等待线程,signalAll()唤醒所有等待线程 - 中断处理:
await()可被中断,需捕获InterruptedException或使用awaitUninterruptibly() - 虚假唤醒:即使没有调用
signal(),线程也可能被虚假唤醒,因此应在循环中检查条件 - 性能考虑:
Condition比wait()/notify()提供了更灵活的控制,但使用不当可能导致死锁
高级暂停技术:ScheduledExecutorService
对于需要周期性执行的任务,ScheduledExecutorService提供了更高级的暂停控制方式。
原理与用法
ScheduledExecutorService可以安排任务在指定延迟后执行,或定期执行,比Thread.sleep()更适合复杂的调度需求。
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorDemo {
public static void main(String[] args) {