SpringBoot之异步调用@Ansyc

在启动类或者线程池配置类上加注解 @EnableAsync

SpringBootApplication
// 开启异步执行
@EnableAsync
public class XFBlogApplication {
    public static void main(String[] args) {
        SpringApplication.run(XFBlogApplication.class, args);
    }
}

定义了线程池的属性类

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;

import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

/**
 * @className: ThreadPoolConfig
 * @description: 线程池配置
 * @author: change
 */
@Configuration
public class ThreadPoolConfig {

       /**
     * 核心线程池大小
     */
    public static final int  corePoolSize = 50;

    /**
     * 最大可创建的线程数
     */
    public static final int maxPoolSize = 200;

    /**
     * 队列最大长度
     */
    public static final int queueCapacity = 1000;

    /**
     * 线程池维护线程所允许的空闲时间
     */
    public static final int keepAliveSeconds = 300;

    /**
     * 线程池中的线程的名称前缀
     */
    public static final String threadNamePrefix = "async-thread-";

    @Bean(name = "threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 1: 创建核心线程数 cpu核数 -- 50
        executor.setCorePoolSize(corePoolSize);
        // 2:线程池维护线程的最大数量,只有在缓存队列满了之后才会申请超过核心线程数的线程
        executor.setMaxPoolSize(maxPoolSize);
        // 3:缓存队列 可以写大一点无非就浪费一点内存空间
        executor.setQueueCapacity(queueCapacity);
        // 4:线程的空闲事件,当超过了核心线程数之外的线程在达到指定的空闲时间会被销毁 200ms
        executor.setKeepAliveSeconds(keepAliveSeconds);
        // 5:线程池中的线程的名称前缀
        executor.setThreadNamePrefix(threadNamePrefix);
        /* 当线程的任务缓存队列已满并且线程池中的线程数量已经达到了最大连接数,如果还有任务来就会采取拒绝策略,
         * 通常有四种策略:
         *ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出异常:RejectedExcutionException异常
         *ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常
         *ThreadPoolExecutor.DiscardOldestPolicy: 丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
         *ThreadPoolExecutor.CallerRunsPolicy:重试添加当前的任务,自动重复调用execute()方法,直到成功。
         *ThreadPoolExecutor. 扩展重试3次,如果3次都不充公在移除。
         * */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
}

需要异步的方法上面加上 @Async

@Component
public class Test {

    @Async("threadPoolTaskExecutor")
    public  void test(int i) throws Exception{
        System.out.println("线程名称: " + Thread.currentThread().getName());
        Thread.sleep(1000);
        System.out.println("参数:"+i);
    }
}

@Async注解失效的情况

  • 异步方法使用static修饰。
    
  • 异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类(因为@Async是spring的注解)。
    
  • 异步方法不能与异步方法在同一个类中。
    
  • 类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象(就以上例来说,得注入service,而不能new)。
    
  • 如果使用SpringBoot框架必须在启动类中/或者线程池固定属性类中,增加@EnableAsync注解。
    
  • 在Async 方法上标注@Transactional是没用的。 在Async 方法调用的方法上标注@Transactional 有效。
    
  • 调用被@Async标记的方法的调用者不能和被调用的方法在同一类中不然不会起作用。
    
  • 使用@Async时要求是不能有返回值的不然会报错的 因为异步要求是不关心结果的。
    

解决事务和异步之间的矛盾

  • 方法A,使用了@Async/@Transactional来标注,但是无法产生事务控制的目的。
    
  • 方法B,使用了@Async来标注, B中调用了C、D,C/D分别使用@Transactional做了标注,则可实现事务控制的目的。
    
end

评论

213
123