基于注解的使用方式包括如下三步:
启动类如下:
@SpringBootApplication
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}
}
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.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 异步线程池
*/
@Configuration
@EnableAsync
public class AsyncExecutorConfig {
/**
* Set the ThreadPoolExecutor's core pool size.
*/
private int corePoolSize = 8;
/**
* Set the ThreadPoolExecutor's maximum pool size.
*/
private int maxPoolSize = 16;
/**
* Set the capacity for the ThreadPoolExecutor's BlockingQueue.
*/
private int queueCapacity = 200;
private String threadNamePrefix = "AsyncExecutor-";
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix(threadNamePrefix);
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
代码中我们通过 ThreadPoolTaskExecutor 创建了一个线程池。参数含义如下所示:
@Slf4j
@Service
public class OrderService {
public static Random random = new Random();
@Autowired
private AsyncTask asyncTask;
public void doShop() {
try {
createOrder();
// 调用有结果返回的异步任务
Future<String> pay = asyncTask.pay();
if (pay.isDone()) {
try {
String result = pay.get();
log.info("异步任务返回结果{}", result);
} catch (ExecutionException e) {
e.printStackTrace();
}
asyncTask.vip();
asyncTask.sendSms();
}
otherJob();
} catch (InterruptedException e) {
log.error("异常", e);
}
}
public void createOrder() {
log.info("开始做任务1:下单成功");
}
/**
* 错误使用,不会异步执行:调用方与被调方不能在同一个类。主要是使用了动态代理,同一个类的时候直接调用,不是通过生成的动态代理类调用
*/
@Async("taskExecutor")
public void otherJob() {
log.info("开始做任务4:物流");
long start = System.currentTimeMillis();
try {
Thread.sleep(random.nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
log.info("完成任务4,耗时:" + (end - start) + "毫秒");
}
}
异步任务服务类
mport lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
import java.util.Random;
import java.util.concurrent.Future;
@Component
@Slf4j
public class AsyncTask {
public static Random random = new Random();
@Async("taskExecutor")
public void sendSms() throws InterruptedException {
log.info("开始做任务2:发送短信");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
log.info("完成任务1,耗时:" + (end - start) + "毫秒");
}
// 返回结果的异步调用
@Async("taskExecutor")
public Future<String> pay() throws InterruptedException {
log.info("开始做异步返回结果任务2:支付");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
log.info("完成任务2,耗时:" + (end - start) + "毫秒");
return new AsyncResult<>("会员服务完成");
}
/**
* 会员积分任务
* @throws InterruptedException
*/
@Async("taskExecutor")
public void vip() throws InterruptedException {
log.info("开始做任务5:会员");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
log.info("开始做异步返回结果任务5,耗时:" + (end - start) + "毫秒");
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = AsyncApplication.class)
public class AsyncApplicationTests {
@Autowired
private OrderService orderService;
@Test
public void testAsync() {
orderService.doShop();
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果展示
2019-05-16 20:25:06.577 [INFO ] [main] - zero.springboot.study.async.service.OrderService-52 开始做任务1:下单成功
2019-05-16 20:25:06.586 [INFO ] [main] - zero.springboot.study.async.service.OrderService-60 开始做任务4:物流
2019-05-16 20:25:06.599 [INFO ] [AsyncExecutor-1] - zero.springboot.study.async.service.AsyncTask-38 开始做异步返回结果任务2:支付
2019-05-16 20:25:13.382 [INFO ] [AsyncExecutor-1] - zero.springboot.study.async.service.AsyncTask-42 完成任务2,耗时:6783毫秒
2019-05-16 20:25:14.771 [INFO ] [main] - zero.springboot.study.async.service.OrderService-68 完成任务4,耗时:8184毫秒
可以看到有的线程的名字就是我们线程池定义的前缀,说明使用了线程池异步执行。其中我们示范了一个错误的使用案例 otherJob(),并没有异步执行。
原因:
spring 在扫描bean的时候会扫描方法上是否包含@Async注解,如果包含,spring会为这个bean动态地生成一个子类(即代理类,proxy),代理类是继承原来那个bean的。此时,当这个有注解的方法被调用的时候,实际上是由代理类来调用的,代理类在调用时增加异步作用。然而,如果这个有注解的方法是被同一个类中的其他方法调用的,那么该方法的调用并没有通过代理类,而是直接通过原来的那个 bean 也就是 this. method,所以就没有增加异步作用,我们看到的现象就是@Async注解无效。
解决方案:
将要异步执行的方法单独抽取成一个类,这样的确可以解决异步注解失效的问题,原理就是当你把执行异步的方法单独抽取成一个类的时候,这个类肯定是被Spring管理的,其他Spring组件需要调用的时候肯定会注入进去,这时候实际上注入进去的就是代理类了,其实还有其他的解决方法,并不一定非要单独抽取成一个类。(当然也有别的方法解决,这里不讨论,可参考博客:https://blog.csdn.net/dongguabai/article/details/80788585)
@Async
public Future doTaskOneAsyncFuture() throws Exception {
System.out.println("开始做任务一");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
return new AsyncResult<>("任务一完成");
}
异步注解代理的方法可以有一个返回值Future,可以用isCancelled判断异步任务是否取消,isDone判断任务是否执行结束,get获取返回结果。
如下方式会使@Async失效
1.在spring.xml配置线程池bean
2.在需要使用多线程的类中注入线程池bean,就可以使用了,下面代码是本人写的一个测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring.xml")
public class ThreadTest {
@Autowired
private TaskExecutor taskExecutor;
@Test
public void testThread(){
taskExecutor.execute(new Runnable() {
public void run() {
for(int i=0;i<10;i++){
try {
System.out.println("线程:"+i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
for(int i=0;i<10;i++){
try {
System.out.println("非线程:"+i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
结果如图
参考文章:
https://blog.csdn.net/weixin_39528789/article/details/80769112
https://blog.csdn.net/qq_28829553/article/details/89384679
https://www.cnblogs.com/nyatom/p/11057659.html#_lab2_1_1
https://blog.csdn.net/mawenshu316143866/article/details/86504689
https://www.jianshu.com/p/fdb4ba80734e
https://www.cnblogs.com/jpfss/p/10273129.html