学习路线
- 1.12创建线程池的方式及阿里规范
- 1.13线程池的扩展
- 1.14线程池的监控
- 1.15线程池关闭
- shutdownNow()
- shutdown()
- 1.16线程池是否需要关闭,怎么关闭?
1.12创建线程池的方式及阿里规范
java.util.concurrent.Executors
Executor,
ExecutorService,
ScheduledExecutorService,
ThreadFactory,
Callable的工厂和工具方法类,提供了一系列的静态工具方法
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();