当前位置: 首页 > news >正文

线程阻塞分析:Object.wait 耗时真相

一、这个火焰图/调用栈告诉我们什么

com.example.Worker.run Self=3000ms └─ java.lang.Object.wait Self=2980ms ⬅ 大头在这

表面现象:Worker.run方法执行了 3 秒,其中2980ms 消耗在Object.wait()上。

关键结论:这不是性能问题,是线程在"等待"


二、Self Time 是什么(重要前提)

在性能分析工具(如 Android Studio Profiler、Perfetto、Async Profiler)中:

概念含义
Total Time该方法及其所有子方法的总耗时
Self Time在这个方法自身代码里花的时间(不含子调用)

举例:

foo() Total=100ms, Self=10ms └─ bar() Total=90ms, Self=90ms

说明foo主要在调用bar,自己只干了 10ms 的活。

回到场景:Object.wait的 Self=2980ms 意味着线程卡在wait()这一行 2.98 秒


三、这不是"CPU 忙",而是"线程阻塞"

关键区分:CPU 时间 vs Wall Clock 时间

类型含义举例
CPU Time(On-CPU)线程真正占用 CPU 执行指令的时间计算、循环
Wall Time(Off-CPU)挂钟时间,包含等待wait/sleep/IO/锁

Object.wait()期间:

  • 线程状态从RUNNABLEWAITING/TIMED_WAITING
  • 线程被挂起,让出 CPU
  • CPU 占用率是 0
  • 挂钟时间在流逝

使用工具时的陷阱

如果你的 Profiler 是"Sample"模式(采样): → 显示的是 Wall Clock 时间 → 会看到 wait 的耗时 如果是"CPU"模式(只算 On-CPU): → wait/sleep 根本不会出现 → 显示"没在干活"

判断你看到的是哪种:如果 wait/sleep 出现在火焰图里且 Self 很大 → 这是Wall Clock / Instrumentation模式。


四、Object.wait()Thread.sleep()的区别

虽然都表现为"线程不干活",但两者本质不同:

特性Object.wait()Thread.sleep()
释放锁✅ 释放当前持有的 monitor❌ 不释放锁
唤醒方式notify()/notifyAll()唤醒到时间自动唤醒
必须在同步块内✅ 是❌ 否
线程状态WAITING / TIMED_WAITINGTIMED_WAITING
用途线程间协作(生产者-消费者)定时延迟
可被 interrupt✅ 是✅ 是

代码对比:

// wait - 释放锁,等待通知synchronized(lock){while(!condition){lock.wait();// 阻塞在这里,锁被释放}}// sleep - 不释放锁,单纯延迟Thread.sleep(3000);// 阻塞 3 秒

五、如何判断"该不该关心"这个 Self Time

✅ 正常情况(不用管)

场景 A:线程池的 Worker 空闲等待任务

publicvoidrun(){while(running){Tasktask=queue.take();// 底层是 waittask.execute();}}

👉 空闲 Worker 卡在take()(内部 wait)是正常的,说明线程池够用。

场景 B:HandlerThread 消息循环

Looper.loop();└─MessageQueue.next()└─ nativePollOnce// 等消息

👉 Handler 线程没消息时挂起,这就是设计

场景 C:主线程 idle

android.os.MessageQueue.nativePollOnceSelf=大量

👉 主线程没事干时等待事件,正常状态


❌ 有问题的情况(需要排查)

问题 1:主线程被 wait 卡住 → ANR 元凶

main thread └─ MyActivity.onClick └─ Object.wait Self=5000ms ⚠️

👉 主线程等 5 秒 → 用户点击后界面卡死 →必然 ANR

问题 2:关键业务线程被 sleep 拖慢

publicvoidloadData(){for(inti=0;i<100;i++){Thread.sleep(100);// 无脑轮询 → 累计 10 秒if(checkReady())return;}}

👉 应该改成事件通知 / CountDownLatch

问题 3:锁竞争严重,大量线程 wait

30 个线程同时: └─ SharedResource.doWork └─ Object.wait ⚠️ (等 monitor)

👉 锁粒度太粗,需要拆分锁或用无锁结构

问题 4:错误的线程通信模式

// 忙等待(反模式)while(!done){Thread.sleep(10);// 轮询}

👉 应该用wait/notifyCountDownLatchFuture


六、如何进一步定位

步骤 1:确认是哪种线程

# 抓 Java 线程栈,看 Worker 线程是什么角色adb shell"kill -3$(pidof com.example.app)"adb pull /data/anr/traces.txt# 或者用 jstack (需要 JDK 工具链)jstack<pid>

看栈的顶部:

"Worker-1" prio=5 tid=0x... nid=0x... in Object.wait() java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x...> (a java.util.LinkedList) at java.lang.Object.wait(Object.java:502) at com.example.Queue.take(Queue.java:42) ⬅ 谁在调 wait at com.example.Worker.run(Worker.java:20)

步骤 2:看是谁调用的 wait

  • 如果是BlockingQueue.take() / poll()线程池空闲,正常
  • 如果是业务代码里的 wait→ 检查是否合理
  • 如果是框架内部(Looper/Handler)→ 正常

步骤 3:切换 Profiler 模式

Android Studio Profiler 三种模式:

模式是否包含 wait/sleep何时用
Sample Java Methods(采样)✅ 会显示 wait找卡顿(Wall Time)
Trace Java Methods(埋点)✅ 会显示 wait精确调用链
Sample C/C++ Functions❌ 只看 CPU找 CPU 热点
System Trace(Perfetto)线程状态色块显示最推荐

👉想找真正的性能问题,用 System Trace,它会区分:

  • 绿色= Running(占用 CPU)
  • 蓝色= Runnable(等 CPU 调度)
  • 橙色= Sleeping(wait/sleep)
  • 红色= Uninterruptible sleep(IO 等)

只有绿色和蓝色才是"CPU 相关问题",橙色的 wait 是正常挂起


七、可视化对比

火焰图看到的(Wall Time 视角)

Worker.run ████████████████████████ 3000ms └─ wait ████████████████████████ 2980ms ← 看起来很吓人 └─ work ▌20ms

System Trace 看到的(真实占用)

CPU 时间轴: Worker ─░░░░░░░░░░░░░░░░░░░░░░░█─ └─ 睡眠(2980ms) └─ 工作(20ms) └─ CPU 占用 = 0 └─ CPU 占用 = 100%

结论:这个线程99.3% 的时间在睡觉,只有 20ms 真正在工作,CPU 是被别人用了


八、实战决策树

看到 Object.wait / Thread.sleep 大 Self Time │ ├─ 是哪个线程? │ ├─ 主线程 → ⚠️ 严重问题,必查 │ ├─ 关键业务线程 → ⚠️ 检查是否合理 │ └─ 线程池 Worker / Handler 线程 → ✅ 通常正常 │ ├─ 调用者是谁? │ ├─ BlockingQueue.take/poll → ✅ 空闲等任务,正常 │ ├─ Looper.loop / nativePollOnce → ✅ 消息循环,正常 │ ├─ CountDownLatch.await → 🤔 检查发送端 │ ├─ 业务代码显式 wait/sleep → ⚠️ 检查逻辑 │ └─ 锁竞争(waiting on monitor) → ⚠️ 优化锁 │ └─ Profiler 是什么模式? ├─ Sample/Trace → 显示 Wall Time,wait 出现正常 └─ System Trace → 看线程状态色块更准确

九、总结要点

  1. Object.wait/Thread.sleep的 Self Time 大 ≠ 性能问题
  2. 本质是"挂钟时间",不是"CPU 时间",线程在挂起状态
  3. 要看是谁在 wait:
    • 空闲的线程池 Worker → 正常
    • 主线程 → 严重问题
    • 显式业务 wait → 具体分析
  4. 推荐用 System Trace(Perfetto)而不是采样 Profiler
  5. 真正的 CPU 热点一般是循环、序列化、加解密、算法等,不会是 wait

http://www.jsqmd.com/news/1368480/

相关文章:

  • GNU Wget2金属链接(Metalink)支持:多镜像源下载与校验实用教程
  • AI 生活化应用设计:从技术到温情的产品化:最小可行方案的范围切分
  • bash-lib测试秘籍:如何用test-utils确保你的Bash函数零缺陷
  • 合肥日结临时工全攻略:渠道、岗位、避坑指南(2026年8月更新) - drfdxr
  • Project-RainMan:终极Swift开源天气应用,让天气预报变得简单直观
  • Pixelle-Video AI全自动短视频引擎:零基础3分钟制作专业视频的终极指南
  • ViT-B-16-SigLIP-512进阶技巧:提升零样本分类性能的10个实用方法
  • Lava框架进阶:自定义神经元模型开发全攻略
  • 预算不花冤枉钱:2026深圳GEO SEO服务商合规性审查报告,附6家主流厂商横向测评 - 品牌前沿专家
  • 2026/8/10-暑期学习日报
  • 38岁Java后端失业三个月,一个被AI时代撞了腰的普通中年程序员,是如何走出焦虑的....
  • Databricks如何把AI编码支出砍掉70%:四个杠杆和一套网关架构
  • Interplanetary Postal Service音效设计:如何用白噪音创造沉浸式太空体验
  • VueLocalStorage核心功能解析:从安装到高级配置的完整教程
  • 20260810 - 暑假热身 总结
  • supa_audit快速入门:3步启用PostgreSQL表变更跟踪功能
  • 如何用SSM快速构建隐马尔可夫模型?3步实现时间序列数据的动态模式识别
  • 革命性提升 SCINet 性能:RevIN 可逆归一化技术完全解析
  • 高效打卡系统设计:从行为心理学到实践工具
  • 海豚善学口碑怎么样,靠谱吗?结合公开信息与学员反馈客观聊聊 - 培训机构评测网
  • 如何在3分钟内安装csview?跨平台安装指南与常见问题解决
  • Shopify是什么?中国卖家做独立站前必须搞清楚的10个问题
  • Comedy框架高级特性:资源管理与依赖注入实战
  • 提升主题质量的7个高级Regularizer:BigARTM实战技巧分享
  • EMBA排行榜涉及项目海外模块时长与学分设置对比
  • 2026年 深圳疑难注销代办机构**:工商异常、税务非正常户、失联吊销快速解决方案深度解析 - 卓企推荐
  • AnimeGarden终极指南:一站式动漫BT资源聚合平台快速上手
  • 为什么选择RT-Thread?5大优势助力物联网项目开发
  • RuoYi-Vue权限管理系统 | 微服务架构下的企业级应用开发平台演进
  • geektime-nginx中的SSL配置:5分钟实现HTTPS安全访问