学习路线

  • 1.12创建线程池的方式及阿里规范
  • 1.13线程池的扩展
  • 1.14线程池的监控
  • 1.15线程池关闭
  • shutdownNow()
  • shutdown()
  • 1.16线程池是否需要关闭,怎么关闭?


1.12创建线程池的方式及阿里规范

java.util.concurrent.Executors
Executor, 
ExecutorService, 
ScheduledExecutorService,
ThreadFactory,
Callable的工厂和工具方法类,提供了一系列的静态工具方法

Java 创建线程监控 自定义java线程池如何监控_Java 创建线程监控


Java 创建线程监控 自定义java线程池如何监控_线程池_02


Java 创建线程监控 自定义java线程池如何监控_java-ee_03

1.13线程池的扩展

*线程池里面提供了几个空方法(钩子方法):
*

beforeExecute、afterExecute和terminated方法,
可以在任务执行前、执行后和线程池关闭前执行一些代码来扩展线程池的行为

比如,任务的平均执行时间、最大执行时间和最小执行时间等,或者输出一些日志信息、发出通知等帮助我们诊断线程池运行时出现的一些问题;通过继承并覆盖线程池的这几个空方法来实现对线程池的扩展;
代码MyThreadPoolExecutor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.*;

/**
 * 自定义线程池对jdk线程池扩展
 *
 */
public class MyThreadPoolExecutor extends ThreadPoolExecutor {

    private static final Logger logger = LoggerFactory.getLogger(MyThreadPoolExecutor.class);

    private final ThreadLocal<Long> startTimeThreadLocal = new ThreadLocal<>();

    public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
    }

    public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
    }

    public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        startTimeThreadLocal.set(System.currentTimeMillis());
        logger.info("线程-{}-任务开始执行时间:{}", t.getName() + t.getId(), startTimeThreadLocal.get());
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        long endTime = System.currentTimeMillis();
        long executeTime = endTime - startTimeThreadLocal.get();
        logger.info("线程-{}-任务执行结束时间:{}ms", Thread.currentThread().getName() + Thread.currentThread().getId(), executeTime);
    }

    @Override
    protected void terminated() {
        super.terminated();
        startTimeThreadLocal.remove();
        logger.info("线程-{}-执行完毕退出.", Thread.currentThread().getName() + Thread.currentThread().getId());
        //TODO 发个通知
    }

    public static void main(String[] args) {
        MyThreadPoolExecutor myThreadPoolExecutor = new MyThreadPoolExecutor(
                8,
                16,
                15,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(512),
                Executors.defaultThreadFactory(),
                new AbortPolicy());

        for (int i = 0; i < 5; i++) {
            myThreadPoolExecutor.execute(() -> {
                logger.info("线程-{}-正在执行......", Thread.currentThread().getName() + Thread.currentThread().getId());
            });
        }
        //关闭线程池
        //myThreadPoolExecutor.shutdown();
    }
}

1.14线程池的监控

线程池在运行过程中出现问题,我们怎么能排查和定位到问题所在呢?监控!
怎么监控?

  • 1、线程池运行时埋点,每一次运行任务都进行采集统计;
  • 2、定时采集线程池的运行数据;
  • 第一种方式的监控是实时监控线程池的运行指标,对性能有一定影响;
  • 第二种方式的监控是定时采集线程池运行数据,并将监控数据持久化,便于后续查看以及用于排查问题;
    线程池的历史运行数据的存储,可以采用时序数据库(TSDB)进行存储,可能大部分公司主要还是用MySQL、ElasticSearch等来存储;
    监控线程池时使用到的属性:
  • taskCount:线程池需要执行的任务数量;
  • completedTaskCount:线程池在运行过程中已完成的任务数量,小于或等于taskCount;
  • largestPoolSize:线程池里曾经创建过的最大线程数量,通过这个数据可以知道线程池是否曾经满过,如该数值等于线程池的最大大小,则表示线程池曾经满过;
  • poolSize:线程池的线程数量,因为默认情况下核心线程不销毁,非核心线程在超时后自动销毁,所以最小也等于核心线程数;
  • activeCount:获取活动的线程数;
    这些数据都可以通过线程池对象给我们提供的API拿到;

1.15线程池关闭

shutdownNow()

  • 1、停止接收新的任务请求;
  • 2、将正在执行的任务interrupt中断;
  • 3、忽略任务队列里面的任务,也就是任务队列里面的任务不会执行了;
  • 4、返回没有执行的任务集合;

shutdown()

  • 1、停止接收新的任务请求;
  • 2、内部正在执行的任务和任务队列中等待的任务会继续执行直至完成;
  • 3、等所有任务都执行完成后才会关闭线程池;

1.16线程池是否需要关闭,怎么关闭?

  • 1、局部线程池一定要shutdown(在代码中声明的临时线程池);
  • 2、全局公用的线程池,不能随便shutdown;
    关闭就是两个方法 shutdown() / shutdownNow();