ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

【JUC】wait 和 sleep

【JUC】wait 和 sleep wait()和sleep()是 Java 多线程中两个非常容易混淆的方法它们都会让当前线程暂停执行但用途、机制和使用场景有本质区别。核心区别一览对比项wait()sleep()所属类ObjectThread是否释放锁释放持有的监视器锁不释放锁调用前提必须在synchronized块/方法中任意位置都可调用唤醒方式notify()/notifyAll()或超时超时自动醒或interrupt()异常抛InterruptedException抛InterruptedException主要用途线程间通信/协作单纯让线程暂停一段时间一、wait()wait()定义在java.lang.Object上因为它是**对象监视器monitor**机制的一部分。每个对象都有一把锁和一个等待队列。关键特性必须持有该对象的锁才能调用否则抛IllegalMonitorStateException。调用后会释放锁让其他线程有机会进入同步块。线程进入该对象的等待集wait set直到其他线程调用同一对象的notify()/notifyAll()或者等待时间超时wait(long timeout)被唤醒后不会立即执行需要重新竞争锁拿到锁后才从wait()返回。标准用法生产者-消费者模式classBuffer{privatefinalQueueIntegerqueuenewLinkedList();privatefinalintCAPACITY5;publicsynchronizedvoidproduce(intvalue)throwsInterruptedException{while(queue.size()CAPACITY){wait();// 队列满释放锁并等待}queue.add(value);notifyAll();// 唤醒等待的消费者}publicsynchronizedintconsume()throwsInterruptedException{while(queue.isEmpty()){wait();// 队列空释放锁并等待}intvaluequeue.poll();notifyAll();// 唤醒等待的生产者returnvalue;}}为什么用 while 而不是 if线程被唤醒后可能条件又不满足了虚假唤醒 spurious wakeup或别的线程抢先消费了所以必须循环检查条件while(条件不满足){wait();}二、sleep()sleep()是Thread的静态方法作用是让当前线程暂停指定时间。关键特性调用时不会释放任何锁——如果持有锁其他线程照样进不来。到时间后线程回到就绪状态等待 CPU 调度。可以被interrupt()打断抛出InterruptedException。Thread.sleep(1000);// 睡 1 秒Thread.sleep(1000,500000);// 1 秒 500 微秒纳秒参数常见用途模拟耗时操作、轮询间隔让出 CPU 给其他线程while(true){// 轮询检查任务if(hasTask()){doTask();}Thread.sleep(500);// 每 500ms 检查一次}三、为什么 sleep 不释放锁看个例子就明白了synchronized(lock){Thread.sleep(5000);// 这 5 秒内其他线程无法进入这个同步块}如果sleep释放了锁同步语义就被破坏了——synchronized保护的临界区可能被其他线程插入执行逻辑就乱了。而wait的设计目的恰恰是主动让出锁去等别人做事所以必须释放。四、记忆口诀wait 是等通知让出锁等别人叫醒我 → 用于线程协作sleep 是打个盹抱着锁睡到点自己醒 → 用于延时五、补充现代替代方案虽然wait/notify是基础但实际开发中更推荐用java.util.concurrent包里的高级工具它们更安全、更易读Condition配合ReentrantLock—— 替代wait/notify支持多个等待队列BlockingQueue—— 直接解决生产者-消费者问题LockSupport.park()/unpark()—— 更底层的线程阻塞原语Lock的底层实现// 用 BlockingQueue 替代手写 wait/notifyBlockingQueueIntegerqueuenewArrayBlockingQueue(5);queue.put(value);// 满了自动阻塞intvqueue.take();// 空了自动阻塞
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进