该方法必须是public 且无返回值,即:
@Async public void asyncMethod(){ }
在和事务一起使用的时候,即调用这个异步方法的方法上使用了@Transactional注解。
a.class
@Async public void asyncMethod(){ }
b.class
@Transactional public void fun(){ //... a.asyncMethod(); //... }
此时事务会有问题,因为a会新起一个线程来执行asyncMethod(),所以a与fun()不是一个事务,会导致数据不一致。
2.需要为异步配置线程池
如下
package com.fanneng.spring.config; import lombok.Getter; import lombok.Setter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.ConfigurationProperties; 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.ThreadPoolExecutor; /** * Created by feifan.gou@gmail.com on 2019/11/29 20:00. */ @Configuration @EnableAsync public class ThreadPoolConfig { @Autowired private ThreadPoolProperty poolProperty; @Bean public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); /* 最大线程数 */ executor.setMaxPoolSize(poolProperty.maxPoolSize); /* 核心线程数 */ executor.setCorePoolSize(poolProperty.corePoolSize); /* 队列最大长度 */ executor.setQueueCapacity(poolProperty.queueCapacity); /* 线程名称前缀 */ executor.setThreadNamePrefix(poolProperty.threadNamePrefix); /* 拒绝策略 */ executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } @Getter @Setter @Configuration @ConfigurationProperties(prefix = "executor.thread.pool") public static class ThreadPoolProperty { private int corePoolSize; private int maxPoolSize; private int queueCapacity; private String threadNamePrefix; } /* 拒绝策略说明(当pool已经达到max size的时候,如何处理新任务): * ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 * ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 * ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程) * ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 */ }
public ThreadPoolTaskExecutor taskExecutor()中的方法名默认是taskExecutor,那么意味着bean的名称也是taskExecutor,那么在使用@Async的时候,会默认去该线程池那线程;如果起了其他的名称,也就是bean是其他名称,那么在使用@Async的时候需要指定线程池的名称;例如bean的名称是 asyncExector ,那么使用@Async的时候应该是
@Async("asyncExector") public void asyncMethod(){ }
否则会去默认使用 SimpleAsyncTaskExecutor。
附上spring 中 TaskExecutor 的常用几个实现
名字 | 特点 |
---|---|
SimpleAsyncTaskExecutor | 每次请求新开线程,没有最大线程数设置.不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。 --【1】 |
SyncTaskExecutor | 不是异步的线程.同步可以用SyncTaskExecutor,但这个可以说不算一个线程池,因为还在原线程执行。这个类没有实现异步调用,只是一个同步操作。 |
ConcurrentTaskExecutor | Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类。 |
SimpleThreadPoolTaskExecutor | 监听Spring’s lifecycle callbacks,并且可以和Quartz的Component兼容.是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类。 |
ThreadPoolTaskExecutor | 最常用。要求jdk版本大于等于5。可以在程序而不是xml里修改线程池的配置.其实质是对java.util.concurrent.ThreadPoolExecutor的包装。 |