如何捕获运行中Java进程的耗时以触发中间告警
解决方案:不中断主进程的耗时监控与告警记录
核心思路
放弃Future.get(timeout)的中断式超时逻辑,改用独立的监控线程/定时任务,仅读取主进程的运行状态(耗时),不干预主进程执行,实现并发记录告警且不中断主进程的目标。
方案一:独立监控线程+共享状态
通过线程安全的共享状态,让监控线程定时检查主进程耗时,触发告警记录。
主进程任务实现
主进程维护启动时间,并通过原子变量标记任务完成状态:
import java.util.Random; import java.util.concurrent.atomic.AtomicBoolean; public class MainProcessTask implements Runnable { private final long startTime; private final AtomicBoolean isCompleted; public MainProcessTask(AtomicBoolean isCompleted) { this.startTime = System.currentTimeMillis(); this.isCompleted = isCompleted; } @Override public void run() { try { // 模拟随数据量变化的业务处理 processData(); } finally { // 任务完成时标记状态 isCompleted.set(true); } } private void processData() { // 实际业务逻辑:处理数据(示例为随机时长睡眠) try { Thread.sleep(new Random().nextInt(30000)); // 0-30秒模拟不同数据量耗时 } catch (InterruptedException e) { // 即使收到中断信号,保留主进程执行(仅恢复中断标记) Thread.currentThread().interrupt(); } } // 获取当前已运行时长 public long getElapsedTime() { return System.currentTimeMillis() - startTime; } }
监控线程实现
定时检查主进程耗时,达到阈值时记录告警,直到主进程完成:
import java.util.concurrent.atomic.AtomicBoolean; public class MonitorTask implements Runnable { private final MainProcessTask mainTask; private final AtomicBoolean isMainCompleted; private static final long ALERT_THRESHOLD = 15000; // 告警阈值:15秒 private static final long CHECK_INTERVAL = 5000; // 检查间隔:5秒 public MonitorTask(MainProcessTask mainTask, AtomicBoolean isMainCompleted) { this.mainTask = mainTask; this.isMainCompleted = isMainCompleted; } @Override public void run() { while (!isMainCompleted.get()) { long elapsed = mainTask.getElapsedTime(); if (elapsed > ALERT_THRESHOLD) { // 记录告警(可替换为日志框架、告警系统调用) System.out.printf("告警:主进程已运行%d毫秒,超过阈值%d毫秒%n", elapsed, ALERT_THRESHOLD); } try { Thread.sleep(CHECK_INTERVAL); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } // 主进程完成后记录最终耗时 System.out.printf("主进程执行完成,总耗时:%d毫秒%n", mainTask.getElapsedTime()); } }
启动代码
import java.util.concurrent.atomic.AtomicBoolean; public class Main { public static void main(String[] args) { AtomicBoolean isCompleted = new AtomicBoolean(false); MainProcessTask mainTask = new MainProcessTask(isCompleted); // 启动主进程线程 Thread mainThread = new Thread(mainTask); mainThread.start(); // 启动监控线程 Thread monitorThread = new Thread(new MonitorTask(mainTask, isCompleted)); monitorThread.start(); // 等待主进程与监控线程完成 try { mainThread.join(); monitorThread.interrupt(); monitorThread.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }
方案二:ScheduledExecutorService定时监控
利用JDK定时调度器替代手动线程管理,更简洁优雅:
import java.util.Random; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public class ScheduledMonitorExample { public static void main(String[] args) { AtomicBoolean isCompleted = new AtomicBoolean(false); MainProcessTask mainTask = new MainProcessTask(isCompleted); long alertThreshold = 15000; // 启动主进程 Thread mainThread = new Thread(mainTask); mainThread.start(); // 创建单线程定时调度器 ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); // 每隔5秒执行一次监控任务(首次执行立即触发) ScheduledFuture<?> monitorFuture = scheduler.scheduleAtFixedRate(() -> { if (isCompleted.get()) { System.out.printf("主进程执行完成,总耗时:%d毫秒%n", mainTask.getElapsedTime()); monitorFuture.cancel(false); // 取消定时任务 scheduler.shutdown(); return; } long elapsed = mainTask.getElapsedTime(); if (elapsed > alertThreshold) { System.out.printf("告警:主进程已运行%d毫秒,超过阈值%d毫秒%n", elapsed, alertThreshold); } }, 0, 5, TimeUnit.SECONDS); // 等待主进程完成并关闭调度器 try { mainThread.join(); scheduler.shutdown(); if (!scheduler.awaitTermination(1, TimeUnit.SECONDS)) { scheduler.shutdownNow(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); scheduler.shutdownNow(); } } // 复用主进程任务类 static class MainProcessTask implements Runnable { private final long startTime; private final AtomicBoolean isCompleted; public MainProcessTask(AtomicBoolean isCompleted) { this.startTime = System.currentTimeMillis(); this.isCompleted = isCompleted; } @Override public void run() { try { processData(); } finally { isCompleted.set(true); } } private void processData() { try { Thread.sleep(new Random().nextInt(30000)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } public long getElapsedTime() { return System.currentTimeMillis() - startTime; } } }
关键注意点
- 线程安全:使用
AtomicBoolean标记任务完成状态,避免多线程下的状态不一致;启动时间startTime为不可变变量,天然线程安全。 - 不中断主进程:监控逻辑仅读取耗时状态,不调用任何中断主进程的方法(如
Future.cancel(true)),确保主进程不受干扰。 - 灵活配置:可根据需求调整告警阈值、检查间隔,告警记录逻辑可替换为日志框架(如SLF4J)、企业告警系统接口等。
内容的提问来源于stack exchange,提问作者Vinay Krishna
相关产品推荐
相关产品推荐

