你该这么做线程池任务监控

简介: 你该这么做线程池任务监控

概述


最近公司项目生产上遇到了一个线程池的问题,由于线程池的配置参数开发人员没有根据实际情况考量,拍脑袋想了一个,将核心线程数和队列数都设置的很大,实际生产提交的任务很多,导致出现CPU很高,影响了其他的一些业务接口。回过头,复盘发现,我们没有对生产的线程池任务做一些监控,比如该任务执行了多长时间,在队列中等待了多少时间等等,那么有什么线程池的编程范式可以监控线程池的任务执行情况呢?


目标


1671173677882.jpg

主要是为了实现下面的2个目标:

  1. 线程池中任务在队列中等待的时间
  2. 线程池中任务运行的时间

我们可以根据实际场景需求打印日志或者通过prometheus输出到监控系统中。


解决方案


我们可以采用装饰者设计模式,写一个线程池任务的包装类,公司项目中所有要提交的任务必须要使用这个包装类去提交任务,定义任务的包装类代码如下:

@Slf4j
public class RunnableWrapper implements Runnable {
    // 实际要执行的线程任务
    private Runnable task;
    // 线程任务被创建出来的时间
    private long createTime;
    // 线程任务被线程池运行的开始时间
    private long startTime;
    // 线程任务被线程池运行的结束时间
    private long endTime;
    // 线程信息
    private String taskInfo;
    // 当这个任务被创建出来的时候,就会设置他的创建时间
    // 但是接下来有可能这个任务提交到线程池后,会进入线程池的队列排队
    public RunnableWrapper(Runnable task, String taskInfo) {
        this.task = task;
        this.taskInfo = taskInfo;
        this.createTime = System.currentTimeMillis();
    }
    // 当任务在线程池排队的时候,这个run方法是不会被运行的
    // 但是当任务结束了排队,得到线程池运行机会的时候,这个方法会被调用
    // 此时就可以设置线程任务的开始运行时间
    @Override
    public void run() {
        this.startTime = System.currentTimeMillis();
        // 此处可以通过调用监控系统的API,实现监控指标上报
        // 用线程任务的startTime-createTime,其实就是任务排队时间
        // 这边打印日志输出,也可以输出到监控系统中
        log.info("任务信息: [{}], 任务排队时间: [{}]ms", taskInfo, startTime - createTime);
        // 接着可以调用包装的实际任务的run方法
        task.run();
        // 任务运行完毕以后,会设置任务运行结束的时间
        this.endTime = System.currentTimeMillis();
        // 此处可以通过调用监控系统的API,实现监控指标上报
        // 用线程任务的endTime - startTime,其实就是任务运行时间
        // 这边打印任务执行时间,也可以输出到监控系统中
        log.info("任务信息: [{}], 任务执行时间: [{}]ms", taskInfo, endTime - startTime);
    }
}

后续我们提交线程池任务统一使用该任务包装类。


使用案例


比如定义一个线程池如下, 核心线程数40, 最大线程数800,线程队列为500:

@Bean(destroyMethod = "shutdown")
    public ThreadPoolExecutor sumTaskThreadPool() {
        return new ThreadPoolExecutor(50, 800, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(500));
    }

定义一个求和的任务:

public class SumRunnable implements Runnable {
    @SneakyThrows
    @Override
    public void run() {
        int sum = 0;
        for (int i = 0; i < 100000; i++) {
            sum = sum + i;
        }
        Thread.sleep(1000);
    }
}

提交任务到线程池中:

@PostConstruct
    public void init() throws InterruptedException {
        // 提交1000个线程任务
        for (int i = 0; i < 1000; i++) {
            // 定义求和任务
            SumRunnable sumRunnable = new SumRunnable();
            // 包装求和任务
            RunnableWrapper runnableWrapper = new RunnableWrapper(sumRunnable, "求和线程任务" + i);
            // 提交任务
            threadPoolExecutor.submit(runnableWrapper);
            Thread.sleep(100);
        }
    }

可以看到打印的结果如下图:

1671173714393.jpg

这样就可以打印出任务的排队时间和执行时间了。


总结


不仅runnable可以进行这样的包装,对于Callable任务也可以进行包装。定义这样的包装类简单,难点在于如何根据打印出的日志进行分析和调整线程池参数。另外就是如何推动公司的同事都使用这样的线程池使用范式也是一大问题。

目录
相关文章
|
10月前
|
存储 Java 数据库
如何处理线程池关闭时未完成的任务?
总之,处理线程池关闭时未完成的任务需要综合考虑多种因素,并根据实际情况选择合适的处理方式。通过合理的处理,可以最大程度地减少任务丢失和数据不一致等问题,确保系统的稳定运行和业务的顺利开展。
417 64
|
10月前
|
消息中间件 监控 Java
线程池关闭时未完成的任务如何保证数据的一致性?
保证线程池关闭时未完成任务的数据一致性需要综合运用多种方法和机制。通过备份与恢复、事务管理、任务状态记录与恢复、数据同步与协调、错误处理与补偿、监控与预警等手段的结合,以及结合具体业务场景进行分析和制定策略,能够最大程度地确保数据的一致性,保障系统的稳定运行和业务的顺利开展。同时,不断地优化和改进这些方法和机制,也是提高系统性能和可靠性的重要途径。
264 62
|
7月前
|
数据采集 Java 数据处理
Python实用技巧:轻松驾驭多线程与多进程,加速任务执行
在Python编程中,多线程和多进程是提升程序效率的关键工具。多线程适用于I/O密集型任务,如文件读写、网络请求;多进程则适合CPU密集型任务,如科学计算、图像处理。本文详细介绍这两种并发编程方式的基本用法及应用场景,并通过实例代码展示如何使用threading、multiprocessing模块及线程池、进程池来优化程序性能。结合实际案例,帮助读者掌握并发编程技巧,提高程序执行速度和资源利用率。
282 0
|
10月前
|
Prometheus 监控 Cloud Native
JAVA线程池监控以及动态调整线程池
【10月更文挑战第22天】在 Java 中,线程池的监控和动态调整是非常重要的,它可以帮助我们更好地管理系统资源,提高应用的性能和稳定性。
566 64
|
8月前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
304 17
|
10月前
|
监控 安全 Java
在 Java 中使用线程池监控以及动态调整线程池时需要注意什么?
【10月更文挑战第22天】在进行线程池的监控和动态调整时,要综合考虑多方面的因素,谨慎操作,以确保线程池能够高效、稳定地运行,满足业务的需求。
223 38
|
10月前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
191 12
|
10月前
|
Prometheus 监控 Cloud Native
在 Java 中,如何使用线程池监控以及动态调整线程池?
【10月更文挑战第22天】线程池的监控和动态调整是一项重要的任务,需要我们结合具体的应用场景和需求,选择合适的方法和策略,以确保线程池始终处于最优状态,提高系统的性能和稳定性。
1594 2
|
11月前
|
缓存 负载均衡 Java
c++写高性能的任务流线程池(万字详解!)
本文介绍了一种高性能的任务流线程池设计,涵盖多种优化机制。首先介绍了Work Steal机制,通过任务偷窃提高资源利用率。接着讨论了优先级任务,使不同优先级的任务得到合理调度。然后提出了缓存机制,通过环形缓存队列提升程序负载能力。Local Thread机制则通过预先创建线程减少创建和销毁线程的开销。Lock Free机制进一步减少了锁的竞争。容量动态调整机制根据任务负载动态调整线程数量。批量处理机制提高了任务处理效率。此外,还介绍了负载均衡、避免等待、预测优化、减少复制等策略。最后,任务组的设计便于管理和复用多任务。整体设计旨在提升线程池的性能和稳定性。
240 5
|
11月前
|
监控 数据可视化 Java
如何使用JDK自带的监控工具JConsole来监控线程池的内存使用情况?
如何使用JDK自带的监控工具JConsole来监控线程池的内存使用情况?