如何定位并解决生产环境偶发的死锁或高线程等待问题
先监控与告警:
暴露线程池/线程数、队列长度、请求并发、响应时间、GC/CPU 等指标(Micrometer/Prometheus/Grafana)。
设置阈值告警(线程数、队列溢出、长时间 blocking)。
采集运行时诊断信息(尽量在线收集,避免重启):
常用命令:jstack -l <pid> > thread-dump.txt、jcmd <pid> Thread.print、Linux: kill -3 <pid>。容器:kubectl exec 到容器里执行。
使用 Java Flight Recorder(JFR)记录锁等待/阻塞(Monitor Wait/Block)并用 Mission Control 分析:jcmd <pid> JFR.start name=rec settings=profile filename=rec.jfr,运行后 JFR.stop。
使用 Arthas(thread、ttop)、async-profiler 或 VisualVM / Mission Control 分析热点和锁竞争。
快速分析要点:
在堆栈中查找 BLOCKED(等待锁)和 WAITING/ TIMED_WAITING 的线程。注意哪个线程持有锁(owner)与等待链(look for cycles)。
判断是“死锁”(互相持有对方锁形成环)还是“线程池耗尽/队列积压/慢阻塞调用”。
检查是否大量同步块/长时间持有锁、阻塞 IO、外部调用(DB、RPC)或定时等待导致线程阻塞。
常见修复策略:
缩小锁粒度 / 减少锁持有时间;使用无锁并发结构(ConcurrentHashMap、LongAdder、Atomic*)。
如果必须加锁,显式使用 ReentrantLock + tryLock(timeout) 或增加超时和报警;保持一致的锁获取顺序以避免循环依赖。
对耗时 IO 使用异步/线程池隔离(bulkhead),为外部调用设置超时与熔断(resilience4j)。
对线程池做合理容量与拒绝策略(bounded queue + rejection handler),监控并扩容或降级。
在关键路径加埋点日志(进入/退出锁、外部调用开始/结束)以便事后追踪。
生产缓解与复现:
若不可立即修复,可临时在生产开启更多诊断(JFR、更多采样频率)、增加线程池或限流,避免连环失效。
在测试环境复现后用更严格的测试(负载+故障注入)验证修复。
事后策略:
将线程转储、JFR、异常堆栈与请求 id 持久化以便事后关联分析。
编写自动检测脚本与告警(发现死锁或长时间 BLOCKED 自动上报)。
运行时周期性检测死锁并把线程堆栈写到日志文件(可部署到线上作为守护进程,低开销)。简单说明:该类每隔 N 秒检查 JVM 是否存在死锁(使用 ThreadMXBean.findDeadlockedThreads),若有则把相关线程堆栈写入文件;同时周期性导出所有线程栈供离线分析
// java import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class DeadlockWatcher implements Runnable { private final ThreadMXBean tm = ManagementFactory.getThreadMXBean(); private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(r -> { Thread t = new Thread(r, "deadlock-watcher"); t.setDaemon(true); return t; }); private final String outFile; private final int periodSeconds; public DeadlockWatcher(String outFile, int periodSeconds) { this.outFile = outFile; this.periodSeconds = periodSeconds; } public void start() { scheduler.scheduleAtFixedRate(this, periodSeconds, periodSeconds, TimeUnit.SECONDS); } public void stop() { scheduler.shutdownNow(); } @Override public void run() { try (PrintWriter pw = new PrintWriter(new FileWriter(outFile, true))) { pw.println("==== Thread Snapshot at " + LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + " ===="); // 检查死锁(包括 ownable synchronizers) long[] deadlocked = tm.findDeadlockedThreads(); // null if none if (deadlocked != null && deadlocked.length > 0) { pw.println("DEADLOCK detected! Thread ids: " + Arrays.toString(deadlocked)); ThreadInfo[] tis = tm.getThreadInfo(deadlocked, true, true); for (ThreadInfo ti : tis) { pw.println(threadInfoToString(ti)); } } else { // 也记录所有线程栈用于分析高等待 ThreadInfo[] all = tm.dumpAllThreads(true, true); for (ThreadInfo ti : all) { pw.println(threadInfoToString(ti)); } } pw.flush(); } catch (IOException e) { // 如果写文件失败,打印到 stderr(尽量不抛异常中断调度) e.printStackTrace(); } } private String threadInfoToString(ThreadInfo ti) { StringBuilder sb = new StringBuilder(); sb.append('"').append(ti.getThreadName()).append("\" Id=").append(ti.getThreadId()) .append(" State=").append(ti.getThreadState()).append('\n'); if (ti.getLockName() != null) { sb.append(" waiting on lock: ").append(ti.getLockName()).append('\n'); } if (ti.getLockOwnerName() != null) { sb.append(" lock owner: ").append(ti.getLockOwnerName()).append(" Id=").append(ti.getLockOwnerId()).append('\n'); } for (StackTraceElement ste : ti.getStackTrace()) { sb.append(" at ").append(ste.toString()).append('\n'); } if (ti.getLockedMonitors() != null && ti.getLockedMonitors().length > 0) { sb.append(" Locked monitors:\n"); Arrays.stream(ti.getLockedMonitors()).forEach(m -> sb.append(" - ").append(m).append('\n')); } if (ti.getLockedSynchronizers() != null && ti.getLockedSynchronizers().length > 0) { sb.append(" Locked synchronizers:\n"); Arrays.stream(ti.getLockedSynchronizers()).forEach(s -> sb.append(" - ").append(s).append('\n')); } return sb.toString(); } // 运行示例:在应用启动处调用 new DeadlockWatcher("`/var/log/app/thread-dump.log`", 30).start(); public static void main(String[] args) { String out = args.length > 0 ? args[0] : "thread-dump.log"; int period = args.length > 1 ? Integer.parseInt(args[1]) : 30; DeadlockWatcher w = new DeadlockWatcher(out, period); w.start(); // keep JVM alive for demo try { Thread.sleep(TimeUnit.MINUTES.toMillis(30)); } catch (InterruptedException ignored) {} w.stop(); } }

浙公网安备 33010602011771号