IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> @Async 注解配合自定义线程池使用及异常处理 -> 正文阅读

[Java知识库]@Async 注解配合自定义线程池使用及异常处理

背景:之前用线程池处理任务的时候都需要手动写类活着匿名内部类实现Runable接口(或者另外两种,懂得都懂),然后重写run方法,个人感觉比较麻烦。
后来偶然一次接触到 Spring 提供的 @Async 注解,便将两者结合起来使用,发现异常依旧可以直接捕获。
Tip:这里只是一个使用的 demo,作者亲测没什么问题。

1. 配置线程池及参数

我平时用的比较多的是 ThreadPoolTaskExecutor,当然用原声的 ThreadPoolExecutor 也是完全没问题的,这里两种我都测了下,都可以。

package com.edward.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.*;

/**
 * @author Edward
 * @date 2022-03-30 17:17
 */
@Configuration
@EnableAsync
@Slf4j
public class ThreadPoolConfig implements AsyncConfigurer {

    /**
     * Spring 封装的线程池
     */
    @Bean("asyncPoolExecutor")
    public ThreadPoolTaskExecutor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(2);
        executor.setKeepAliveSeconds(60);
        executor.setQueueCapacity(2);
        executor.setThreadNamePrefix("edward-thread-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        executor.setAllowCoreThreadTimeOut(true);
        executor.initialize();
        return executor;
    }

    /**
     * juc 原生线程池
     */
    @Bean("threadPoolExecutor")
    public ThreadPoolExecutor threadPoolExecutor() {
        BlockingQueue<Runnable> runnables = new LinkedBlockingQueue<>(2);
        return new ThreadPoolExecutor(
                2,
                2,
                30,
                TimeUnit.SECONDS,
                runnables,
                new ThreadPoolExecutor.AbortPolicy()
        );

    }

    /**
     * 让 @Async 注解识别
     */
    @Override
    public Executor getAsyncExecutor() {
    	// 需要替换的话自己改一下方法名即可
        return threadPoolExecutor();
    }

}

2. 编写需要线程池处理的业务逻辑

package com.edward.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

/**
 * @author Edward
 * @date 2022-03-30 17:51
 */
@Slf4j
@Service
public class AsyncService {
	// 这里指定之前定义的线程池名称即可
    @Async(value = "threadPoolExecutor")
    public void testAsync() {
    	// 注意:这里处理的异常时当前方法执行的异常
        try {
            log.info("thrad-name:{},before to do...", Thread.currentThread().getName());
            // 模拟业务执行时间
            Thread.sleep(2000);
            log.info("thrad-name:{},after to do...", Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3. 调用异步方法

package com.edward.controller;

import com.edward.service.AsyncService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author Edward
 * @date 2022-03-30 17:17
 */
@RestController
@RequiredArgsConstructor
@Slf4j
public class AsyncTestController {
    private final ThreadPoolExecutor executor;
    private final AsyncService asyncService;

    @GetMapping(value = "/testAsync")
    public String testAsync(){
    	// 这里拿到的是线程池本身出现的异常,但是拿不到方法内部的异常
        try {
            asyncService.testAsync();
        } catch (RejectedExecutionException e) {
            log.error("come to RejectedExecutionHandler,{},{}",
                    executor.getActiveCount(),
                    executor.getMaximumPoolSize(),e);
        } catch (Exception exception) {
            log.error("come to Exception",exception);
        }
        return null;
    }
}

4. 注意点:

  1. 以上三步 controller 中拿到的异常为线程池本身的异常,比如走进了拒绝策略等;
  2. 以上三步 service 中处理的异常是该方法本身可能出现的异常;
  3. 关键点:servcie 层中的异常无法在 controller 中捕获!!!。
  4. 若想在 controller 中捕获 service 层中的异常:
    方法一:老老实实的重写 run方法,不要用 @Async注解;
    方法二:可以在 controller 中用 Future 获取异步方法的返回值,然后在 service 层中返回的时候对结果做处理(不过这种处理方式比较麻烦,比重写更麻烦)。

5. 补充(异常获取)

下面重写的方法是在这个类中
public class ThreadPoolConfig implements AsyncConfigurer{}

使用 @Async 注解后,在异步方法中抛出的异常,再重写 AsyncUncaughtExceptionHandler 方法即可捕捉,但无法给前端(作者暂时没找到方法)

@Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new AsyncUncaughtExceptionHandler() {
            @Override
            public void handleUncaughtException(Throwable ex, Method method, Object... params) {
                log.error("=========come to handleUncaughtException,", ex);
            }
        };
    }

Tip:这种捕捉方式没有太大的意义,无法让 @ControllerAdvice获取(作者实验下来是这样的,当然如果有办法还请评论区告诉我);不如直接在异步方法中使用 try{…}catch{…} 捕捉。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-31 23:49:43  更:2022-03-31 23:51:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:24:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码