线程池参数
1. corePoolSize - 常驻核心线程数,即使空闲时仍保留在池中的线程数,除非设置allowCoreThreadTimeOut为true
2. maximumPoolSize - 池中允许的最大线程数。必须大于或等于1,如果等于corePoolSize那就是固定大小线程池。队列缓存达到上限后,如果还有新任务需要处理,线程池就会创建新的线程。
3. keepAliveTime -表示线程池中线程的空闲时间,当空闲时间达到这个值,线程会被销毁,知道只剩下corePoolSize个线程为止,避免浪费内存和句柄资源
4. unit - keepAliveTime-参数的时间单位
5. workQueue - 用于在执行任务之前使用的队列。 这个队列将仅保存 execute 方法提交的 Runnable任务。当请求线程数大于核心线程数时,线程进入 BlockingQueue 阻塞队列。建议使用有界队列,可以增加系统的稳定性和预警能力
6. threadFactory - 用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字,可实现ThreadFactory 接口,自定义线程工厂
7. handler - 饱和策略,执行被阻止时使用的处理程序,因为达到线程限制和队列容量,也是一种简单的限流保护
AbortPolicy:直接丢弃并抛出异常(默认情况)
CallerRunsPolicy:只用调用者所在线程来运行任务
DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务
DiscardPolicy:不处理,丢弃掉
自定义策略需实现RejectedExecutionHandler接口 :友好的拒绝策略有三种:保存到数据库进行削峰填谷,在空闲时再提取出来执行;转向某个提示页面;打印日志
线程预热
通过调用线程池中prestartAllCoreThreads()和prestartCoreThread()进行核心线程的创建,prestartAllCoreThreads()会创建所有核心线程数,prestartCoreThread()只会创建一个
作用
- 降低资源消耗
- 提高响应速度
- 提高线程的可管理性
线程池简单demo
优雅关闭线程池方法:
利用JVM钩子函数,在虚拟机关闭时调用相关方法即”优雅关闭线程池”。
先通过shutdown等待线程池自身结束,然后等待一段时间,如果没有成功,再调用shutdownNow将等待I/O的任务中断并退出。
shutdown():关闭线程池,将线程池的状态设置为SHUTWDOWN状态,根据拒绝策略拒绝后续提交的任务,继续执行正在的执行的线程以及阻塞队列中的线程,直到所有线程执行完毕
shutdownNow():关闭线程池,将线程池的状态设置为STOP,根据拒绝策略拒绝后续提交的任务,会给所有线程池中的线程发送 interrupt 中断信号,尝试中断这些任务的执行,然后会将任务队列中正在等待的所有任务转移到一个 List 中并返回,我们可以根据返回的任务 List 来进行一些补救的操作,例如记录在案并在后期重试
一般不建议直接使用shutdownNow(),可能会导致正在执行的任务出错
public class ThreadPoolUtils {
/**
* 使用volatile关键字保其可见性
*/
private static volatile ThreadPoolExecutor threadPool = null;
/** 线程本地变量,用于记录线程异步任务的开始执行时间*/
private static final ThreadLocal<Long> START_TIME = new ThreadLocal<>();
/**
* 无返回值直接执行
*
* @param runnable
*/
public static void execute(Runnable runnable,int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit) {
getThreadPool().execute(runnable);
}
/**
* 返回值直接执行
*
* @param callable
*/
public static <T> Future<T> submit(Callable<T> callable) {
return getThreadPool().submit(callable);
}
private static ThreadPoolExecutor getThreadPool() {
if (threadPool == null) {
synchronized (ThreadPoolUtils.class) {
// 二次检查
if (threadPool == null) {
// 创建实例之前可能会有一些准备性的耗时工作
sleepSecond(300L);
// 获取处理器数量
int cpuNum = Runtime.getRuntime().availableProcessors();
// 根据cpu数量,计算出合理的线程并发数
int threadNum = cpuNum * 2 + 1;
threadPool = new ThreadPoolExecutor(
// 核心线程数
threadNum - 1,
// 最大线程数
threadNum,
// 闲置线程存活时间
Integer.MAX_VALUE,
// 时间单位
TimeUnit.MILLISECONDS,
// 线程队列
new LinkedBlockingDeque<Runnable>(Integer.MAX_VALUE),
// 线程工厂
Executors.defaultThreadFactory(),
// 队列已满,而且当前线程数已经超过最大线程数时的异常处理策略
new ThreadPoolExecutor.AbortPolicy() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
super.rejectedExecution(r, e);
}
}
){
//继承:调度器终止钩子
@Override
protected void terminated()
{
System.out.println(("调度器已经终止!"));
}
//继承:执行前钩子
@Override
protected void beforeExecute(Thread t, Runnable target)
{
System.out.println((target + "前钩被执行"));
//记录开始执行时间
START_TIME.set(System.currentTimeMillis());
super.beforeExecute(t, target);
}
//继承:执行后钩子
@Override
protected void afterExecute(Runnable target, Throwable t)
{
super.afterExecute(target, t);
//计算执行时长
long time = (System.currentTimeMillis() - START_TIME.get()) ;
System.out.println((target + " 后钩被执行, 任务执行时长(ms):" + time));
//清空本地变量
START_TIME.remove();
}
};
}
}
}
return threadPool;
}
public static void shutdownThreadPoolGracefully(ExecutorService threadPool) {
// 若已经关闭则返回
if (threadPool == null || threadPool.isTerminated()) {
return;
}
try {
threadPool.shutdown(); //拒绝接受新任务
} catch (SecurityException | NullPointerException e) {
return;
}
try {
// 等待60秒,等待线程池中的任务完成执行
if (!threadPool.awaitTermination(60L, TimeUnit.SECONDS)) {
// 调用 shutdownNow() 方法取消正在执行的任务
threadPool.shutdownNow();
// 再次等待60秒,如果还未结束,可以再次尝试,或者直接放弃
if (!threadPool.awaitTermination(60L, TimeUnit.SECONDS)) {
System.err.println("线程池任务未正常执行结束");
}
}
} catch (InterruptedException ie) {
// 捕获异常,重新调用 shutdownNow()方法
threadPool.shutdownNow();
}
// 仍然没有关闭,循环关闭1000次,每次等待10毫秒
if (!threadPool.isTerminated()) {
try {
for (int i = 0; i < 1000; i++) {
if (threadPool.awaitTermination(10, TimeUnit.MILLISECONDS)) {
break;
}
threadPool.shutdownNow();
}
} catch (Throwable e) {
System.err.println(e.getMessage());
}
}
}
private void SeqOrScheduledTargetThreadPoolLazyHolder(ExecutorService threadPool) {
//注册JVM关闭时的钩子函数
Runtime.getRuntime().addShutdownHook(
new ShutdownHookThread((InternalLogger) log,
(Callable<Void>) () -> {
//优雅地关闭线程池
shutdownThreadPoolGracefully(threadPool);
return null;
}));
}
public static final void sleepSecond(long seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
log.warn(e.toString());
}
}
}
下面参考Rocketmq中代码
public class ShutdownHookThread extends Thread {
private volatile boolean hasShutdown = false;
private AtomicInteger shutdownTimes = new AtomicInteger(0);
private final InternalLogger log;
private final Callable callback;
/**
* Create the standard hook thread, with a call back, by using {@link Callable} interface.
*
* @param log The log instance is used in hook thread.
* @param callback The call back function.
*/
public ShutdownHookThread(InternalLogger log, Callable callback) {
super("ShutdownHook");
this.log = log;
this.callback = callback;
}
/**
* Thread run method.
* Invoke when the jvm shutdown.
* 1. count the invocation times.
* 2. execute the {@link ShutdownHookThread#callback}, and time it.
*/
@Override
public void run() {
synchronized (this) {
log.info("shutdown hook was invoked, " + this.shutdownTimes.incrementAndGet() + " times.");
if (!this.hasShutdown) {
this.hasShutdown = true;
long beginTime = System.currentTimeMillis();
try {
this.callback.call();
} catch (Exception e) {
log.error("shutdown hook callback invoked failure.", e);
}
long consumingTimeTotal = System.currentTimeMillis() - beginTime;
log.info("shutdown hook done, consuming time total(ms): " + consumingTimeTotal);
}
}
}
}