首页 > 编程笔记 > Java笔记 阅读:1

Java监控线程池的2种方法(附带示例)

在生产环境中,我们可以实时监控线程池的运行状态,随时掌握应用服务的性能状况,以便在系统资源紧张时及时告警,动态调整线程配置,并在必要时进行人工介入,排查问题,线上修复,也就是说,通过实时监控实现动态修改。

线程池的监控方法分为两种:

全量监控线程池

为了实现全量监控,我们可以通过扩展 ThreadPoolExecutor 类并重写 before-Execute() 和 afterExecute() 两个方法来记录时间,示例实现代码如下:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicLong;
public class TimingThreadPoolExecutor extends ThreadPoolExecutor {
    private final ThreadLocal<Long> startTime = new ThreadLocal<>();
    private final AtomicLong totalTime = new AtomicLong();
    private final AtomicLong numTasks = new AtomicLong();
    public TimingThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                           TimeUnit unit,BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }
    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        startTime.set(System.nanoTime());
    }
    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        try {
            long endTime = System.nanoTime();
            long taskTime = endTime - startTime.get();
            numTasks.incrementAndGet();
            totalTime.addAndGet(taskTime);
            System.out.printf("任务耗时: %dns\n", taskTime);
        } finally {
            super.afterExecute(r, t);
        }
    }
    public void shutdownAndReport() {
        System.out.printf("平均任务耗时: %dns\n", totalTime.get() / numTasks.get());
        super.shutdown();
    }
}

下面我们利用 TimingThreadPoolExecutor 工具类监控线程池,方法如下:
public class TimingThreadPoolExample {
    public static void main(String[] args) {
        TimingThreadPoolExecutor executor = new TimingThreadPoolExecutor(
                1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
        executor.submit(() -> {
            // 模拟任务执行时间
            try {
                TimeUnit.SECONDS.sleep(1);
           } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
           }
       });
       executor.shutdownAndReport();
   }
}
在上述示例中,beforeExecute() 方法在任务开始前记录了开始时间,而 afterExecute() 方法在任务结束时计算了任务耗时并将其输出。

定时监控线程池

定时监控可以通过一个独立的监控线程来实现,该线程周期性地从线程池获取状态信息并记录或报告这些信息。示例实现代码如下:
import java.util.concurrent.*;
public class ThreadPoolStatusMonitor implements Runnable {
    private final ThreadPoolExecutor executor;
    private final int monitoringPeriod;
    private boolean running = true;
    public ThreadPoolStatusMonitor(ThreadPoolExecutor executor, int monito-ringPeriod) {
        this.executor = executor;
        this.monitoringPeriod = monitoringPeriod;
    }
    public void shutdown() {
        running = false;
    }
    @Override
    public void run() {
        while (running) {
            System.out.println(
                String.format("[监控] 活跃线程:%d, 核心线程数:%d,
                               最大线程数:%d, 队列任务数:%d, 完成任务数:%d",
                    executor.getActiveCount(),
                    executor.getCorePoolSize(),
                    executor.getMaximumPoolSize(),
                    executor.getQueue().size(),
                    executor.getCompletedTaskCount())
            );
            try {
                TimeUnit.SECONDS.sleep(monitoringPeriod);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

下面我们利用 ThreadPoolStatusMonitor 工具类定时监控线程池,在主程序中,我们创建并启动监控线程,方法如下:
public class ThreadPoolMonitoringExample {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                5, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
        // 启动线程池状态监控线程,每2s监控一次
        ThreadPoolStatusMonitor monitor = new ThreadPoolStatusMonitor(executo-r, 2);
        Thread monitorThread = new Thread(monitor);
        monitorThread.start();
        // 提交一些任务给线程池
        for (int i = 0; i < 10; i++) {
            executor.submit(() -> {
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }
        // 模拟程序运行一段时间后,关闭监控线程和线程池
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        monitor.shutdown();
        executor.shutdown();
    }
}
在上述示例中,我们创建了一个 ThreadPoolStatusMonitor 实例监控线程池。它会每隔一定时间输出一次线程池的各种状态信息。通过这种方式,我们可以监控线程池的状态,以确保它正常运行且在运行中保持性能稳定。

我们简化了上述两个监控示例的实现过程,在实际生产中,为了提升性能和实现可视化,可以利用 Kafka 获取监控数据,然后制作一个可视化界面来展示这些数据,这样更有利于根据监控数据状态进行线程池优化。

相关文章