挂起、阻塞、锁和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) 包装,确保主线程安全。

繁忙等待浪费了处理器资源,换作使用java的等待通知机制是更佳的设计选择。即需要等待时应该挂起等待。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值