> 文档中心 > 【并发编程七:Java中的线程池(4)-线程池规范、监控、关闭】

【并发编程七:Java中的线程池(4)-线程池规范、监控、关闭】

【衔接上一章 【并发编程六:Java中的线程池(3)-线程池的应用】】

学习路线

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

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

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

【并发编程七:Java中的线程池(4)-线程池规范、监控、关闭】
【并发编程七:Java中的线程池(4)-线程池规范、监控、关闭】
【并发编程七:Java中的线程池(4)-线程池规范、监控、关闭】

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();

【衔接下一章【并发编程八:线程安全问题分析及锁的介绍(1)】】