Java 怎么获取多线程信息、父线程下的子线程
后端面里 Java 线程题很常见,有的公司会追问「线上怎么查线程」「父子线程关系怎么拿」。结合平时排查 CPU 飙高、线程 dump 的经验记一下。
题目
- Java 怎么获取多线程信息?
- 怎么获取父线程下的子线程?
参考答案(口述要点)
- 本进程内:
Thread.currentThread()、Thread.getAllStackTraces()、ThreadMXBean- 监控/运维:
jstack、Arthasthread、JFR- 父子关系:JDK 里线程是 1:1 映射内核线程,没有官方「子线程列表」API;用户创建的「子线程」需自己登记,或通过
Thread.getAllStackTraces()过滤ThreadGroup
详细解析
1. 当前线程与基础属性
Thread t = Thread.currentThread();
System.out.println(t.getName());
System.out.println(t.getId()); // 唯一 id(注意与线程池复用区分)
System.out.println(t.getState()); // NEW、RUNNABLE、BLOCKED、WAITING…
System.out.println(t.isDaemon());
System.out.println(t.getPriority());2. 进程内所有线程(含栈)
Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
for (Map.Entry<Thread, StackTraceElement[]> e : map.entrySet()) {
Thread thread = e.getKey();
System.out.println(thread.getName() + " state=" + thread.getState());
for (StackTraceElement ste : e.getValue()) {
System.out.println(" at " + ste);
}
}生产环境更常用 jstack、Arthas(thread -n 3)看最忙的几个线程,避免在业务代码里频繁 dump。
3. ThreadMXBean(JMX,适合监控埋点)
ThreadMXBean mx = ManagementFactory.getThreadMXBean();
long[] ids = mx.getAllThreadIds();
ThreadInfo[] infos = mx.getThreadInfo(ids, Integer.MAX_VALUE);
for (ThreadInfo info : infos) {
if (info == null) continue;
System.out.printf("%s blocked=%d waited=%d%n",
info.getThreadName(), info.getBlockedCount(), info.getWaitedCount());
}
// 死锁检测
long[] deadlocked = mx.findDeadlockedThreads();
if (deadlocked != null) {
ThreadInfo[] dl = mx.getThreadInfo(deadlocked);
// 打印 dl 定位锁顺序
}4. 父线程与子线程
thread.getName()/ 自定义 ThreadFactory 命名,便于日志对齐。- ThreadGroup:老 API,子线程可设同一 group,
group.enumerate(threads)枚举(现在更少用)。 - 没有
parent.getChildThreads():若业务要「跟踪某任务派生的线程」,需在线程池提交时 自己维护「父任务 → Future 列表」 或用 结构化并发(JDK 21+StructuredTaskScope)。
// 示例:线程池里提交,用 Future 列表代替「子线程枚举」
ExecutorService pool = Executors.newFixedThreadPool(4);
List<Future<String>> futures = new ArrayList<>();
for (int i = 0; i < 10; i++) {
final int idx = i;
futures.add(pool.submit(() -> "ok-" + idx));
}
for (Future<String> f : futures) {
System.out.println(f.get());
}
pool.shutdown();5. 虚拟线程(JDK 21+,面试可提一句)
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Subtask<String> a = scope.fork(() -> loadA());
Subtask<String> b = scope.fork(() -> loadB());
scope.join();
scope.throwIfFailed();
String result = a.get() + b.get();
}父子生命周期由 scope 统一管理,比手动记线程 id 靠谱。
代码与实践(项目场景)
| 场景 | 做法 |
|---|---|
| 接口突然变慢、CPU 高 | top -Hp pid 找线程 tid → jstack 看是否死循环、锁竞争 |
| 怀疑线程泄漏 | 对比多次 jstack,看某线程名数量是否持续增长;线程池是否未 shutdown |
| 异步任务要可追溯 | MDC 透传(Slf4j)+ 线程池用 TtlExecutors(TransmittableThreadLocal)保证 traceId 不丢 |
写在最后
面试别背「getParentThread」这种不存在的 API;说清楚 JVM 提供的观测手段 + 线上 jstack/Arthas + 业务自己用线程池/Future 管理任务 就够像干过活的。
创作不易,转载请注明出处和作者。
