package com.laker.admin.framework.ext.thread; import cn.hutool.core.thread.ThreadUtil; import lombok.extern.slf4j.Slf4j; import net.bull.javamelody.internal.common.LOG; import java.util.concurrent.*; /** * 自定义扩展线程池用于捕获执行中异常,防止异常被吞 *
* 强烈建议使用EasyAdminMDCThreadPoolExecutor *
* * @author laker */ @Slf4j public class EasyAdminThreadPoolExecutor extends ThreadPoolExecutor { /** * 简易线程池构造器 * * @param poolSize 线程池大小,e.g:60 * @param queueSize 等待队列,e.g:1000 * @param prefix 线程名前缀,e.g:MCP-POOL */ public EasyAdminThreadPoolExecutor(int poolSize, int queueSize, String prefix) { super(poolSize, poolSize, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(queueSize), ThreadUtil .newNamedThreadFactory(prefix + "-", false), new EasyAdminRejectPolicy()); // 非核心线程如果处于闲置状态超过该值,就会被销毁。如果设置allowCoreThreadTimeOut(true),则会也作用于核心线程。 this.allowCoreThreadTimeOut(true); } public EasyAdminThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue* * 这个实现什么也不做,但可以在子类中定制。 * 注意:为了正确地嵌套多个重写,子类通常应该在该方法结束时调用super.beforeexecute。 * 在给定线程中执行给定的可运行项之前调用的方法。 * 此方法由将执行任务r的线程t调用,并可用于重新初始化线程局部变量或执行日志记录。 * * * 如果beforeExecute抛出一个RuntimeException,那么任务将不被执行,并且afterExecute也不会被调用 * 参数 t:t将运行任务r的线程 * 参数 r:r将要执行的任务 **/ @Override protected void beforeExecute(Thread t, Runnable r) { super.beforeExecute(t, r); log.debug("beforeExecute-线程:{},任务:{}", t, r); } /** *
* XXX 这里虽然有兜底的抓取异常,但是也会导致线程的销毁重建,表现为 线程大小2 A-1,A-2 会一直递增 。。A-100 * 所以最好还是在 POOL.execute()中加入try catch包裹 * 这个实现什么也不做,但可以在子类中定制。 * 注意:为了正确地嵌套多个重写,子类通常应该在这个方法的开头调用super.afterexecute。 * 在执行给定的可运行文件完成时调用的方法. * * 无论任务是从run中正常返回,还是抛出一个异常而返回,afterExecute都会被调用 * 如果任务在完成后带有一个Error,那么就不会调用afterExecute * * 参数 r 如果是 submit提交的 任务,或者是FutureTask任务,则异常不在参数t中,而是如下方式获取 **/ @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); if (t == null && r instanceof Future) { try { // Object result = ((Future>) r).get(); ((Future>) r).get(); } catch (CancellationException ce) { t = ce; } catch (ExecutionException ee) { t = ee.getCause(); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); // ignore/reset } } if (t != null) { log.error("线程池中的任务执行异常!!!", t); } log.debug("afterExecute-任务:{},异常:{}", r, t); } /** *
* exec.shutdown(); * 当执行器终止时调用的方法。默认实现什么也不做。 * 注意:为了正确地嵌套多个重写,子类通常应该在这个方法中调用super.terminated。 * 在线程池完成关闭时调用terminated * 也就是在所有任务都已经完成并且所有工作者线程也已经关闭后 * ,terminated可以用来释放Executor在其生命周期里分配的各种资源,此外还可以执行发送通知、记录日志或者手机finalize统计等操作 **/ @Override protected void terminated() { super.terminated(); LOG.debug("线程池关闭鸟!!"); } }