@Async避坑

我们平时使用@Async是这么使用的

@SpringBootApplication
@EnableAsync
public class DemoApplication {
@Component
public class AsyncService {
    @Async
    public void async(){
        try {
            Thread.sleep(1000);
            System.out.println(Thread.currentThread());
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
}
@ResponseBody
@GetMapping("test")
public void test(){
    service.async();
}

这样会有一个问题

在每一次方法调用里,都创建了一个单独的线程池去处理。这样的话,如果请求量一增加,整个操作系统的压力就会耗尽,最终所有的业务都无法响应

让我们看下原因,访问时,直接打个断点,即可获取执行异步线程的线程池

 可以看到,异步任务使用了一个线程池,它的corePoolSize=8, 阻塞队列采用了无界队列LinkedBlockingQueue。一旦采用了这样的组合,最大线程数就会形同虚设,因为超出8个线程的任务,将全部会被放到无界队列里。

我们可以通过以下3种方式避免

1,properties文件声明最大的线程数

spring.task.execution.pool.core-size=5
spring.task.execution.pool.max-size=50
spring.task.execution.pool.queue-capacity=200
spring.task.execution.thread-name-prefix=test

2,创建这自己的ThreadPoolExceute 

@Configuration
//@EnableAsync
public class SyncConfiguration {
    @Bean(name = "otherTaskExecutor")
    public ThreadPoolTaskExecutor executor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        //核心线程数
        taskExecutor.setCorePoolSize(10);
        //线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        taskExecutor.setMaxPoolSize(100);
        //缓存队列
        taskExecutor.setQueueCapacity(50);
        //许的空闲时间,当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
        taskExecutor.setKeepAliveSeconds(200);
        //异步方法内部线程名称
        taskExecutor.setThreadNamePrefix("other-");
        /**
         * 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略
         * 通常有以下四种策略:
         * ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
         * ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
         * ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
         * ThreadPoolExecutor.CallerRunsPolicy:重试添加当前的任务,自动重复调用 execute() 方法,直到成功
         */
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        taskExecutor.initialize();
        return taskExecutor;
    }
}

3,实现接口创建自己的ThreadPoolExceute 

@Configuration
//@EnableAsync
@Slf4j
public class AsyncConfiguration implements AsyncConfigurer {

    @Bean(name = "defaultPoolTaskExecutor")
    public ThreadPoolTaskExecutor executor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        //核心线程数
        taskExecutor.setCorePoolSize(2);
        //线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        taskExecutor.setMaxPoolSize(10);
        //缓存队列
        taskExecutor.setQueueCapacity(50);
        //许的空闲时间,当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
        taskExecutor.setKeepAliveSeconds(200);
        //异步方法内部线程名称
        taskExecutor.setThreadNamePrefix("default-");
        /**
         * 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略
         * 通常有以下四种策略:
         * ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
         * ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
         * ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
         * ThreadPoolExecutor.CallerRunsPolicy:重试添加当前的任务,自动重复调用 execute() 方法,直到成功
         */
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        taskExecutor.initialize();
        return taskExecutor;
    }

    /**
     * 指定默认线程池
     */
    @Override
    public Executor getAsyncExecutor() {
        return executor();
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (ex, method, params) ->
                log.error("线程池执行任务发送未知错误,执行方法:{}",method.getName(),ex);
    }

然后在用异步注解时指定名字,就可以避免,请求量一增加,整个操作系统的压力就会耗尽,最终所有的业务都无法响应

@Async("defaultPoolTaskExecutor")

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
@Async方法是Spring框架的一种特殊注解,用于实现异步方法调用。通过在方法上添加@Async注解,可以告诉Spring容器该方法应该在单独的线程执行,而不会阻塞当前线程的执行。\[1\] 要使用@Async方法调用,需要按照以下步骤进行操作: 1. 在启动类上添加@EnableAsync注解,以启用异步功能。\[2\] 2. 在需要异步执行的方法上添加@Async注解。这样,当调用该方法时,Spring会自动将其放入线程执行,并返回一个Future对象,用于获取异步方法的执行结果。\[3\] 例如,假设我们有一个名为doSomething的方法需要异步执行,我们可以在方法上添加@Async注解,如下所示: ```java @Async public Future<String> doSomething() { // 异步执行的逻辑 return new AsyncResult<>("异步方法执行结果"); } ``` 在调用doSomething方法时,它将在单独的线程执行,并返回一个Future对象。我们可以使用该对象来获取异步方法的执行结果,如下所示: ```java Future<String> futureResult = doSomething(); String result = futureResult.get(); // 获取异步方法的执行结果 ``` 通过使用@Async方法调用,我们可以实现并发执行任务,提高系统的响应性能和吞吐量。同时,它也可以帮助我们处理那些不需要立即返回结果的耗时操作,提高系统的并发处理能力。 #### 引用[.reference_title] - *1* *3* [在Spring使用Future对象调用Async方法调用](https://blog.csdn.net/dnc8371/article/details/106705454)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Spring使用@Async异步调用方法](https://blog.csdn.net/qq_34178998/article/details/95939425)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值