jstack 1 | grep -A 12 'pool-1-thread' | head -50 jcmd 1 Thread.print -l | grep -c WAITING
批量任务跑了 40 分钟没动静,jstack 里 8 个线程都是 WAITING,一个停在 Future.get,另外几个在等队列里的子任务被调度。这类现象通常不是突然出现的,往前翻半天日志能看到苗头。
追下去是这么回事:父任务占着线程等子任务结果,子任务又要同一个池的空闲线程才能跑。池被父任务占满,子任务永远排不上,双方谁也不让。
围绕「父子任务死锁」排查时,命令行跑一遍比在面板里点十次有用,面板会替你做掉不少隐式操作。同类问题如果隔一段时间又出现,多半是某个定时任务或重启流程把改动冲掉了。
把这次用到的命令存进笔记,比记住结论有用,换个环境还能照着走。
处理上按这个顺序:父任务不要在池内同步等子任务 改成全部提交后统一等待;父子任务用两个独立的池 避免争同一批线程;或者用 CompletableFuture 的组合方法 不阻塞工作线程;用 jstack 连续抓两次 确认线程栈没有变化。
确认没问题再收工,同样的批次量 4 分钟跑完,jstack 里不再出现 Future.get 长时间挂起。
