一、前言

异步注解@Async是实现我们程序提高效率的很重要的注解。在方法上添加@Async注解,表示此方法是异步方法, 在类上添加@Async表示类中所有方法都是异步方法,使用此注解的类,必须是Spring管理的类,需要在启动类或配置中加入@EnableAsync注解,@Async才会生效。

在使用Async时,如果不指定线程池的名称,也就是不是自定义线程池,@Async是有默认线程池的,使用的是Spring默认的线程池SimpleAsyncTaskExecutor。

默认线程池的默认配置如下:

00001. 默认核心线程数:8;

00002. 最大线程数:Integet.MAX_VALUE;

00003. 队列使用LinkedBlockingQueue;

00004. 容量是:Integet.MAX_VALUE;

00005. 空闲线程保留时间:60s;

线程池拒绝策略:AbortPolicy

二、基本使用

1.添加依赖

<dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>31.1-jre</version>
        </dependency>

2.自定义线程池设置

package com.example.spepcdemo.config;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.*;

/**
 * @author qx
 * @date 2024/6/3
 * @des 自定义线程池设置
 */
@EnableAsync
@Configuration
public class AsyncTaskConfig {

    @Bean("my-executor")
    public Executor executor() {
        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("my-executor").build();
        //获取CPU的处理器数量
        int curSystemThreads = Runtime.getRuntime().availableProcessors() * 2;
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(curSystemThreads, 100, 200, TimeUnit.SECONDS, new LinkedBlockingDeque<>(), threadFactory);
        threadPoolExecutor.allowsCoreThreadTimeOut();
        return threadPoolExecutor;

    }

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

}

3.异步服务类

package com.example.spepcdemo.service;

import cn.hutool.core.thread.ThreadUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

/**
 * @author qx
 * @date 2024/5/28
 * @des
 */
@Slf4j
@Service
public class AsyncService {


    /**
     * 异步任务1
     */
    @Async("async-executor")
    public void async1() {
        ThreadUtil.execAsync(() -> {
            log.info("异步执行操作1");
            ThreadUtil.sleep(2000);
        });

    }

    /**
     * 异步任务2
     */
    @Async("async-executor")
    public void async2() {
        ThreadUtil.execAsync(() -> {
            log.info("异步执行操作2");
            ThreadUtil.sleep(3000);
        });
    }
}

4.创建测试类

package com.example.spepcdemo.controller;

import com.example.spepcdemo.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author qx
 * @date 2024/5/28
 * @des 异步操作
 */
@RestController
public class AsyncController {

    @Autowired
    private AsyncService asyncService;

    @GetMapping("/async")
    public void testAsync() {
        long start = System.currentTimeMillis();
        //调用两个异步方法
        asyncService.async1();
        asyncService.async2();
        System.out.println("完成操作,耗时:" + (System.currentTimeMillis() - start));
    }
}

5.测试

我们启动程序在浏览器上访问http://localhost:8080/async。

异步注解@Async自定义线程池_Async

6.主要事项

@Async失效有几个原因:

1. 注解@Async的方法不是public方法;

2. 注解@Async的返回值只能为void或Future;

3. 注解@Async方法使用static修饰也会失效;

4. 没加@EnableAsync注解;

5. 调用方和@Async不能在一个类中;

6. 在Async方法上标注@Transactional是没用的,但在Async方法调用的方法上标注@Transcational是有效的;