挂起、阻塞、锁和cpu占用

张开发
2026/4/10 23:33:42 15 分钟阅读

分享文章

挂起、阻塞、锁和cpu占用
Thread.sleep()和Object.wait()在 Java 多线程编程中Thread.sleep()和Object.wait()都能让线程暂停执行但它们的目的机制和使用场景有本质区别。‌核心区别总结‌‌所属类不同‌sleep()是 ‌Thread类的静态方法‌作用于当前线程。wait()是 ‌Object类的实例方法‌作用于某个对象的监视器锁。‌是否释放锁‌最核心区别sleep()‌不会释放任何锁‌线程仍持有同步块或方法中的锁。wait()‌会立即释放当前对象的锁‌允许其他线程获取该锁并执行同步代码。‌调用前提‌sleep()可在‌任意位置调用‌无需同步上下文。wait()‌必须在synchronized代码块或方法中调用‌否则抛出IllegalMonitorStateException。‌唤醒方式‌sleep()在指定时间结束后‌自动恢复‌也可被interrupt()中断。wait()需要其他线程调用同一对象的notify()或notifyAll()才能唤醒也可带超时参数如wait(1000)或被中断唤醒。‌线程状态‌sleep()使线程进入 ‌TIMED_WAITING‌ 状态是一种挂起函数‌它使线程暂时挂起进入TIMED_WAITING状态。wait()无参使线程进入 ‌WAITING‌ 状态带超时的wait(long)进入 ‌TIMED_WAITING‌。‌典型使用场景‌sleep()用于‌延迟执行‌、控制轮询间隔、模拟耗时操作等‌单线程节奏控制适用于需要‌定时暂停‌的场景如限速、模拟延迟等‌。wait()用于‌线程间协作与通信‌如生产者-消费者模型等待某条件满足后再继续。‌使用建议‌若只需‌让线程暂停一段时间‌用Thread.sleep()。若需‌等待某个条件成立并与其他线程协作‌用wait()notify()/notifyAll()并配合while循环检查条件以避免虚假唤醒。‌切勿在非同步上下文中调用wait()‌否则运行时异常。‌避免用线程对象调用sleep()‌如t.sleep()应使用Thread.sleep()因它是静态方法语义上属于“当前线程休眠”。‌Thread.sleep()方法会阻塞线程但不会占用 CPU 资源‌核心结论‌不占用 CPU‌调用Thread.sleep()后当前线程会进入‌阻塞BLOCKED/TIMED_WAITING状态‌JVM 会将其从调度队列中移除‌不再分配 CPU 时间片‌因此‌不会消耗 CPU 资源‌‌。‌释放 CPU 控制权‌线程主动让出 CPU操作系统可将 CPU 分配给其他就绪线程有助于降低整体 CPU 负载‌。补充说明‌仍持有锁‌虽然释放了 CPU但 ‌Thread.sleep()不会释放已获取的同步锁如synchronized或ReentrantLock‌这与Object.wait()有本质区别‌。‌线程池中的影响‌在使用线程池时调用sleep()的线程‌仍占用线程池中的一个线程槽位‌无法执行其他任务可能降低线程池吞吐量‌。‌上下文切换开销‌线程从阻塞恢复为就绪状态时操作系统需进行上下文切换此过程有轻微开销但‌不属于Thread.sleep()直接导致的 CPU 占用‌‌。常见误区❌ “sleep()占用 CPU” —— 实际是‌未使用 sleep 的忙等待busy-wait才导致高 CPU 占用‌‌。✅ 正确做法在循环中加入Thread.sleep()可显著降低 CPU 使用率例如从 100% 降至 3% 左右‌。综上‌Thread.sleep()是一种高效、低开销的线程暂停机制适用于需要延时或降低 CPU 负载的场景‌。Kotlin 协程的挂起不会阻塞线程‌这是协程区别于传统线程阻塞机制的核心优势之一。关键要点‌挂起suspend‌ 是协程内部的状态暂停仅暂停当前协程的执行‌不会阻塞底层线程‌线程可继续执行其他任务。‌阻塞blocking‌ 是指线程被占用无法执行其他工作直到操作完成如Thread.sleep()或同步 I/O。协程通过 ‌Continuation 机制‌ 和 ‌状态机‌ 实现挂起与恢复在挂起点保存上下文后释放线程待异步操作完成后再调度恢复执行。实际影响在 Android 开发中使用suspend函数如withContext(Dispatchers.IO)执行网络请求或数据库操作时‌主线程不会被阻塞‌UI 保持响应。若在主线程直接调用阻塞式 API如同步 HTTP 请求‌即使在外层启动了协程仍会导致 ANR‌因此必须配合withContext(Dispatchers.IO)等调度器使用。总结✅ ‌挂起 非阻塞‌协程挂起时线程被让出可处理其他协程。❌ ‌阻塞 线程占用‌应避免在主线程或协程中使用阻塞式调用如Thread.sleep()、同步 I/O。 最佳实践所有可能耗时的操作网络、文件、数据库应在suspend函数内使用withContext(Dispatchers.IO)包装确保主线程安全。

更多文章