【Java线程】线程池的拒绝策略、异常捕获 您所在的位置:网站首页 线程池拒绝策略分别使用在什么场景 【Java线程】线程池的拒绝策略、异常捕获

【Java线程】线程池的拒绝策略、异常捕获

2023-12-26 03:17| 来源: 网络整理| 查看: 265

目录标题 拒绝策略1、拒绝策略使用场景2、JDK内置拒绝策略3、自定义线程池创建方式4、自定义拒绝策略1.简单示例2.JAVA线程池自定义拒绝策略以及利用反射获取任务参数 线程池异常处理1、run函数中的try catch是必须的2、其他异常捕获1.通过设置UncaughtExceptionHandler打印异常日志2.双层try catch嵌套(项目中的使用)

使用线程池之前,先思考下面几个问题:

该异步场景适合用线程还是线程池?使用JAVA默认的线程池,还是自定义的线程池?使用JAVA默认的拒绝策略,还是自定义的拒绝策略?当Java提供的拒绝策略无法满足我们的业务场景时,需要我们自定义拒绝策略。涉及到自定义拒绝策略的场景的业务就比较复杂了,一般来说肯定是不想抛弃该任务而做的补偿措施。是否需要捕获拒绝策略异常?一般捕获的目的是为了打印日志,方便后期做些追踪、调查。

针对第二点,我们需要慎重再慎重。你需要思考,你的业务场景对任务是否执行到底有多敏感。以「金融项目」为例,每次的任务都涉及到金钱,当任务队列满的时候,新的任务肯定是不能抛弃的啊。新任务放不到任务队列时,这时候我们应该采取补偿措施,比如把新任务放到一个「阻塞队列」里面,然后新任务等待被执行。

拒绝策略 1、拒绝策略使用场景

Q: 什么时候需要使用拒绝策略呢? A: 当任务数量超过系统实际承载能力的时候就要用到拒绝策略了,可以说它是系统超负荷运行的补救措施。简言之,就是线程用完,队列已满,无法为新任务服务,则需一套机制来合理的处理这些问题。

2、JDK内置拒绝策略

JDK 提供了四种内置拒绝策略,我们要理解并记住,有如下的四种: 1、DiscardPolicy: 默默丢弃无法处理的任务,不予任何处理 2、DiscardOldestPolicy: 丢弃队列中最老的任务, 尝试再次提交当前任务 3、AbortPolicy(默认): 直接抛异常,阻止系统正常工作。 4、CallerRunsPolicy: 将任务分给调用线程来执行,运行当前被丢弃的任务,这样做不会真的丢弃任务,但是提交的线程性能有可能急剧下降。

3、自定义线程池创建方式 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { ... }

以下是我们通常的使用方式(自定义线程池):

方式1: 线程池创建之后,指定拒绝策略. 如果不知道则使用默认的拒绝策略AbortPolicy。

int corePoolSize = 1; int maximumPoolSize = 1; BlockingQueue queue = new ArrayBlockingQueue(1); ThreadPoolExecutor pool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,0, TimeUnit.SECONDS, queue ) ; pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy ()); pool.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy()); pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

方式2: 创建线程池的时候指定拒绝策略

private static final ThreadPoolExecutor NEW_MESSAGE_COUNT_POOL = new ThreadPoolExecutor( 8, 20, 5, TimeUnit.MINUTES, new ArrayBlockingQueue(16), new ThreadPoolExecutor.DiscardPolicy() );

方式3: 把线程池当做一个Compent注入到Spring容器中使用。

@Configuration public class ExecutorConfiguration { @Bean public ThreadPoolExecutor taskAuditSavePoolExecutor(){ return new ThreadPoolExecutor(2, 5, 1L, TimeUnit.MINUTES, new ArrayBlockingQueue(10),new ThreadPoolExecutor.DiscardPolicy()); } @Bean public ThreadPoolExecutor emailPullAllExecutor(MessagePullAllExecutorConfig config){ return new ThreadPoolExecutor( config.getCorePoolSize(), config.getMaximumPoolSize(), config.getKeepAliveTime(), TimeUnit.SECONDS, new ArrayBlockingQueue(config.getWorkQueueCapacity(), new ThreadPoolExecutor.DiscardPolicy()) ); } } 4、自定义拒绝策略 1.简单示例 通过实现RejectedExecutionHandler接口,自定义一个拒绝策略类,重写它的rejectedExecution()方法: public class CustomRejectionHandler implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.out.println(r.toString() + "被拒绝了,执行入库操作,之后手动补偿"); } }

测试下:

public class SpringbootApplication { private static AtomicInteger atomicInteger = new AtomicInteger(1); public static void main(String[] args) throws Exception { CustomRejectionHandler customRejectionHandler = new CustomRejectionHandler(); ExecutorService executorService = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, new LinkedBlockingDeque(10), customRejectionHandler); for (int i = 0; i @Override public void run() { System.out.println("第" + atomicInteger.getAndIncrement() + "个任务被执行"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } }

结果: 在这里插入图片描述

2.JAVA线程池自定义拒绝策略以及利用反射获取任务参数

https://blog.csdn.net/baidu_17353319/article/details/121532270

这个链接好好看,里面说明了不同的拒绝策略底层代码的实现逻辑。

线程池异常处理 1、run函数中的try catch是必须的

runnable里面的 run 函数里面的逻辑必须 try…catch

//如果用的是submit,如果这里不加try catch 会导致异常被”吃掉“ PULL_TASK_EXECUTOR.execute(new Runnable() { @Override public void run() { try{ // ...业务任务。 }catch (Exception e){ } } }); 2、其他异常捕获 1.通过设置UncaughtExceptionHandler打印异常日志

该方式我没有模拟过。

1、

//如果是线程模式 Thread t = new Thread(); t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread t, Throwable e) { LOGGER.error(t + " throws exception: " + e); } }); //如果是线程池的模式: ExecutorService threadPool = Executors.newFixedThreadPool(1, r -> { Thread t = new Thread(r); t.setUncaughtExceptionHandler( (t1, e) -> LOGGER.error(t1 + " throws exception: " + e)); return t; });

2、自定义线程池如何捕获线程异常

2.双层try catch嵌套(项目中的使用)

我在项目中是通过双层try catch使用的:

@Slf4j @Service public class DemoServiceImpl implements DemoService { private ThreadPoolExecutor taskAuditSavePoolExecutor = new ThreadPoolExecutor(2, 5, 1L, TimeUnit.MINUTES, new ArrayBlockingQueue(10)); @Override public void testThreadPool(String param) { // 第一层:捕获线程池抛出的异常。 典型的是:拒绝策略抛出的异常。RejectedExecutionException是最顶层的拒绝策略异常接口。 try { taskAuditSavePoolExecutor.execute(() -> { // 第二层:捕获任务内部发生的异常。 try { log.info("param:{}", param); Thread.sleep(10000); } catch (Exception e) { log.error( "error, param:{}", param ); } }); }catch (RejectedExecutionException rejectedExecutionException) { log.info("exception,start execute rejection policy,param:{}", param); } } } @SpringBootTest class EmailDemoApplicationTests { @Autowired DemoService demoService; @Test void contextLoads() { for (int i = 0; i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有