Skip to content

The ThreadPool of Java(II)

Posted on:March 21, 2023 at 11:21 AM
Share on

Executors 是创建线程池的工具类,比较典型常见的四种线程池包括: newFixedThreadPool 、 newSingleThreadExecutor 、 newCachedThreadPool 、 newScheduledThreadPool。每一种都有自己特定的典型例子,可以按照每种的特 性用在不同的业务场景,也可以做为参照精细化创建线程池。 但是一般大厂都不允许使用 Executors 创建线程池!这么创建的话,控制不好会出现 OOM。

Table of contents

Open Table of contents

四种线程池使用介绍

newFixedThreadPool

ExecutorService executorService = Executors.newFixedThreadPool(3);

newFixedThreadPool 执行过程

newSingleThreadExecutor

ExecutorService executorService = Executors.newSingleThreadExecutor();

newSingleThreadExecutor 执行过程

newCachedThreadPool

ExecutorService executorService = Executors.newCachedThreadPool();

newCachedThreadPool 执行过程

newScheduledThreadPool

ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);

newScheduledThreadPool 执行过程

线程池使用场景说明

使用线程池可以降低服务器资源的投入, 让每台机器尽可能更大限度的使用 CPU。

线程池的使用会随着业务场景变化而不同,如果你的业务需要大量的使用 线程池,并非常依赖线程池,那么就不可能用 Executors 工具类中提供的方法。 因为这些线程池的创建都不够精细化,也非常容易造成 OOM 风险,而且随着业务 场景逻辑不同,会有 IO 密集型和 CPU 密集型。

最终,大家使用的线程池都是使用 new ThreadPoolExecutor() 创建的,当然也 有基于 Spring 的线程池配置 org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor。

可你想过吗,同样一个接口在有活动时候怎么办、有大促时候怎么办,可能你当 时设置的线程池是合理的,但是一到流量非常大的时候就很不适合了,所以如果 能动态调整线程池就非常有必要了。而且使用 new ThreadPoolExecutor() 方式 创建的线程池是可以通过提供的 set 方法进行动态调整的。有了这个动态调整的方法后,就可以把线程池包装起来,在配合动态调整的页面,动态更新线程池 参数,就可以非常方便的调整线程池了。

获取线程池监控信息

可监控内容

方法说明
getActiveCount线程池中正在执行任务的线程数量
getCompletedTaskCount线程池已完成的任务数量,该值小于等于 taskCount
getCorePoolSize线程池的核心线程数量
getLargestPoolSize线程池曾经创建过的最大线程数量。通过这个数据可以知 道线程池是否满过,也就是达到了 maximumPoolSize
getMaximumPoolSize线程池的最大线程数量
getPoolSize线程池当前的线程数量
getTaskCount线程池已经执行的和未执行的任务总数

重写线程池方式监控

如果我们想监控一个线程池的方法执行动作,最简单的方式就是继承这个类,重 写方法,在方法中添加动作收集信息。

public class ThreadPoolMonitor extends ThreadPoolExecutor {
  @Override
  public void shutdown() {
    // 统计已执行任务、正在执行任务、未执行任务数量
    super.shutdown();
  }
  @Override
  public List<Runnable> shutdownNow() {
    // 统计已执行任务、正在执行任务、未执行任务数量
    return super.shutdownNow();
  }
  @Override
  protected void beforeExecute(Thread t, Runnable r) {
    // 记录开始时间
  }
  @Override
  protected void afterExecute(Runnable r, Throwable t) {
    // 记录完成耗时
  }

}

基于 JVMTI 方式监控

这块是监控的重点,因为我们不太可能让每一个需要监控的线程池都来重写的方 式记录,这样的改造成本太高了。

那么除了这个笨方法外,可以选择使用基于 JVMTI 的方式,进行开发监控组件。 JVMTI:JVMTI(JVM Tool Interface)位于 jpda 最底层,是 Java 虚拟机所提供的 native 编程接口。JVMTI 可以提供性能分析、debug、内存管理、线程分析等功 能。

基于 jvmti 提供的接口服务,运用 C++代码(win32-add_library)在 Agent_OnLoad 里开发监控服务,并生成 dll 文件。开发完成后在 java 代码中加入 agentpath, 这样就可以监控到我们需要的信息内容。

Share on