springboot中开启@Async异步支持:
其实,@Async的异步调用从spring3.x之后就开始支持了,springboot中当然也可以使用。
1、配置AsyncConfig配置类
package com.tingcream.springmybatis.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@EnableAsync//开启Async异步调用支持
@Configuration
public class AsyncConfig {
/**
* 自定义异步线程的执行器 ,springboot中默认寻找的执行器名称为"taskExecutor"
*/
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize(10);
// 设置最大线程数
executor.setMaxPoolSize(20);
// 设置队列容量
executor.setQueueCapacity(20);
// 设置线程活跃时间(秒)
executor.setKeepAliveSeconds(60);
// 设置线程名称前缀
executor.setThreadNamePrefix("async-task-thread-");
return executor;
}
}
2、AsyncController(测试controller层)
package com.tingcream.springmybatis.async.controller;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.tingcream.springmybatis.async.service.AsyncService;
import com.tingcream.springmybatis.common.RetMsg;
/**
* 异步调用测试 controller层
* @author jelly
*/
@RestController
@RequestMapping("/async")
public class AsyncController {
@Autowired
private AsyncService asyncService ;
@RequestMapping("/test1")
public RetMsg<?> test1(String name) {
long t1=System.currentTimeMillis();
Future<String> n = asyncService.getName(name);
Future<Integer> age = asyncService.getAge();
Map<String,Object> map =new HashMap<String,Object>();
try {
map.put("name", n.get());// 调用Feture<V>对象的get方法会产生阻塞,直到有结果返回
map.put("age", age.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
long t2=System.currentTimeMillis();
long sec= (t2-t1)/1000;
System.out.println("耗时:"+sec+"秒");
map.put("ts",sec+"秒"); //4秒
return RetMsg.success(map);
}
}
3、AsyncService(测试service层)
package com.tingcream.springmybatis.async.service;
import java.util.concurrent.Future;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
/**
* 异步调用测试 service层
* @author jelly
*
*/
@Service
public class AsyncService {
@Async
public Future<String> getName(String name) {
try {
System.out.println("线程名:"+Thread.currentThread().getName());
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// return AsyncResult.forValue("hi:"+name) ;
return new AsyncResult<>("hi:"+name);
}
@Async
public Future<Integer> getAge() {
try {
System.out.println("线程名:"+Thread.currentThread().getName());
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new AsyncResult<>(22);
}
}
浏览器请求: http://localhost:8081/async/test1?name=zhangsan
控制台输出:
4、关于线程池的执行及对象代理的说明
Spring中通过Async代理对象来执行异步任务
在spring中,@Async注解既可以标记在类上也可以标记在方法上。如果标记在类上则表示类中的所有方法均为异步的(相当于所有的类中所有方法都标记了@Async主机),标记在方法上则此方法时异步的。无论是标记在类上还是方法上,类中只要有一个标记了@Async注解,spring都会为这个bean创建动态代理对象,这个代理对象会寻找线程执行器从而启动一个线程来执行方法中的内容。由于代理对象是基于bean来动态创建而不是基于bean的每个方法创建的,因此在@Async的bean中,异步A方法中调用异步的B方法,仍然是同步调用的。
Spring中默认的TaskExecutor是"taskExecutor"
@Async注解中还可以指定value的值,可通过bean名称来指定调用的线程池, springboot默认会使用名称为"taskExecutor"的线程池,如果没有找到,才会使用其他类型为TaskExecutor或其子类的线程池。例如:@Async("MyExecutor") 就可以改为使用其他的线程池对象去执行异步任务。
Spring中的TaskExecutor子类都有哪些
- SimpleAsyncTaskExecutor 这种实现不会重用任何线程,每次调用都会创建一个新的线程。
- SyncTaskExecutor 这种实现不会异步的执行。
- ConcurrentTaskExecutor 这种实现是java.util.concurrent.Executor的一个adapter。
- SimpleThreadPoolTaskExecutor 这种实现实际上是Quartz的SimpleThreadPool的一个子类,它监听Spring的声明周期回调。
- ThreadPoolTaskExecutor 这是最常用最通用的一种实现。它包含了java.util.concurrent.ThreadPoolExecutor的属性,并且用TaskExecutor进行包装。