近期在组内做了一次spring的异步分享,将内容记录下来。
转载请注明出处:https://blog.csdn.net/dhtx_wzgl/article/details/85229397!
0、概念
同步、异步和阻塞、非阻塞
1、spring异步实现
2、spring阻塞异步的使用
环境最低要求:spring3.2+(3.0开始支持服务层异步,3.2开始支持控制层异步)、servlet3.0+、tomcat7.0+、java6+
本文demo环境:spring5、servlet3.1、tomcat8、java8
建议环境:spring5、servlet3.1、tomcat8.5、java8
2.1 服务层异步
2.1.1 使用
1)、启动类或者配置类上加@EnableAsync,或者直接通过xml配置
配置类方法
@Configuration
@EnableAsync
public class SpringAsyncConfig { ... } |
xml配置方法
参数解析:
executor:指定一个缺省的executor给@Async使用
proxy-target-class:是否使用基于类的代理,默认为false
参数名 |
类型 |
是否必填 |
描述 |
id |
string |
必填 |
线程池实例名,唯一标识 |
pool-size |
string |
可选 |
值既可以是单值也可以是一个范围(m-n),如果任务队列无界,那么n无效,但是m=0时除外,此时,coresize=n,allowCoreThreadTimeout=true;当队列有界时,coresize=m,maxsize=n;默认值为coresize=1,maxsize=Integer.MAX_VALUE;当为单值时(pool-size="n"),coresize=n,maxsize=Integer.MAX_VALUE。 |
queue-capacity |
string |
可选 |
任务队列,默认值为Integer.MAX_VALUE,即无界 |
keep-alive |
string |
可选 |
线程超时时间 |
rejection-policy |
string |
可选 |
任务 拒绝策略:
- ABORT(缺省):抛出TaskRejectedException异常,然后不执行
- DISCARD:不执行,也不抛出异常
- DISCARD_OLDEST:丢弃queue中最旧的那个任务
- CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
|
2)、在被调用的方法或所属类上加上@Async注解
//类声明上加注解
@Service
@Slf4j
@Async
public class AsyncService {
//方法声明上加注解
@Async
public void say(String var) throws InterruptedException { |
3)、
a、调用方法和被调用方法必须在不同的类中,因为调用同一个类中的方法时,被调用方法上的注解都会失效。
b、@Async与@Transactional同时使用时,事物不起作用。
2.1.2 demo
@Slf4j
public class SleepUtil {
private static Random random = new Random();
public static void sleep() throws InterruptedException {
int timeOut = random.nextInt(3000);
log.info("休眠时间:{}ms",timeOut);
Thread.sleep(timeOut);
}
}
@Service
@Slf4j
@Async
public class AsyncService {
/**
* 无返回值
*
* @param var
* @throws InterruptedException
*/
public void say(String var) throws InterruptedException {
log.info("开始执行无返回值阻塞异步方法");
Stopwatch stopwatch = Stopwatch.createStarted();
sleep();
log.info("say:{}", var);
log.info("完成无返回值阻塞异步方法执行,参数:{},耗时:{}", var, stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
/**
* 普通返回值
*
* @param var
* @return
* @throws InterruptedException
*/
public String sayHasRetrun(String var) throws InterruptedException {
log.info("开始执行普通返回值阻塞异步方法");
Stopwatch stopwatch = Stopwatch.createStarted();
sleep();
log.info("say:{}", var);
log.info("完成普通返回值阻塞异步方法执行,参数:{},耗时:{}", var, stopwatch.elapsed(TimeUnit.MILLISECONDS));
return "看到结果没?";
}
/**
* future返回值
*
* @param var
* @return
* @throws InterruptedException
*/
public Future sayForFuther(String var) throws InterruptedException {
log.info("开始执行回调返回值阻塞异步方法");
Stopwatch stopwatch = Stopwatch.createStarted();
sleep();
log.info("say:{}", var);
log.info("完成回调返回值阻塞异步方法执行,参数:{},耗时:{}", var, stopwatch.elapsed(TimeUnit.MILLISECONDS));
return new AsyncResult<>("FutureResult:" + var);
}
}
public class AysncServiceTest extends BaseTest {
@Resource
private AsyncService aysncService;
@Test
public void say() throws Exception {
Stopwatch stopwatch = Stopwatch.createStarted();
aysncService.say("hello");
LOGGER.info("result:用时:{}ms",stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
@Test
public void sayHasRetrun() throws Exception {
Stopwatch stopwatch = Stopwatch.createStarted();
String result = aysncService.sayHasRetrun("hello");
LOGGER.info("result:{},用时:{}ms",result,stopwatch.elapsed(TimeUnit.MILLISECONDS));
Thread.sleep(1000);
}
@Test
public void sayForFuther() throws Exception {
Stopwatch stopwatch = Stopwatch.createStarted();
Future future = aysncService.sayForFuther("hello");
LOGGER.info("result:{},用时:{}ms",stopwatch.elapsed(TimeUnit.MILLISECONDS),future.get());
Thread.sleep(1000);
}
} |
2.2 控制层异步
2.2.1 使用
1)、在web.xml中的所有servlet和filter声明中添加true,例如
watcher
qunar.ServletWatcher
true
springmvc
org.springframework.web.servlet.DispatcherServlet
1
true
|
2)、true只在3.0版本以上的xml配置文件,所以需要将低版本的web.xml文件头改为3.0或以上版本
3)、controller接口返回值必须是回调接口,比如Callable、DeferredResult等。
2.2.2 demo
@RestController
@Slf4j
public class DemoController {
@Resource
private NormalService normalService;
@GetMapping("/async")
public Callable async(final String var) throws InterruptedException, ExecutionException {
Stopwatch stopwatch = Stopwatch.createStarted();
Callable callable = new Callable() {
@Override
public String call() throws Exception {
return "hello "+var;
}
};
log.info("async used time {} ms",stopwatch.elapsed(TimeUnit.MILLISECONDS));
return callable;
}
@GetMapping("/sync")
public String get(String var) throws InterruptedException, ExecutionException {
Stopwatch stopwatch = Stopwatch.createStarted();
String result = normalService.sayHasRetrun(var);
log.info("sync used time {} ms",stopwatch.elapsed(TimeUnit.MILLISECONDS));
return result;
}
}
@Service
@Slf4j
public class NormalService {
public String sayHasRetrun(String var) throws InterruptedException {
log.info("开始执行普通返回值阻塞方法");
Stopwatch stopwatch =Stopwatch.createStarted();
sleep();
log.info("say:{}",var);
log.info("完成普通返回值阻塞方法执行,参数:{},耗时:{}",var,stopwatch.elapsed(TimeUnit.MILLISECONDS));
return "看到结果没?";
}
}
@Slf4j
public class DemoControllerTest extends MockTest {
@Test
public void async() throws Exception {
MvcResult mvcResult = mockMvc.perform(
get("/async2")
.param("var", "async"))
.andReturn();
// LOGGER.info("返回结果:{}", mvcResult.getResponse().getContentAsString());
LOGGER.info("返回结果:{}", mvcResult.getAsyncResult(3000));
}
@Test
public void sync() throws Exception {
MvcResult mvcResult = mockMvc.perform(
get("/sync")
.param("var", "sync"))
.andReturn();
LOGGER.info("返回结果:{}", mvcResult.getResponse().getContentAsString());
}
}
|
3、扩展阅读
spring webflux 学习
spring webflux: http://www.liuhaihua.cn/archives/512708.html
异步非异步:https://juejin.im/post/5b4cd263e51d4519846971e0
servlet: https://www.jianshu.com/p/c23ca9d26f64
reactor: https://www.ibm.com/developerworks/cn/java/j-cn-with-reactor-response-encode/index.html
spring5异步:http://www.infoq.com/cn/articles/Servlet-and-Reactive-Stacks-Spring-Framework-5
spring@Async:https://blog.csdn.net/ClementAD/article/details/47403185
spring@Async执行源码:https://www.jianshu.com/p/6a3c335b8839
srping控制层异步: https://yq.aliyun.com/articles/622413?utm_content=m_1000011297
spring控制层异步源码分析:https://blog.csdn.net/woshilijiuyi/article/details/79316105
DeferredResult: https://www.jianshu.com/p/acd4bbd83314
Callable vs DeferredResult :https://www.cnblogs.com/aheizi/p/5659030.html
CGLIB :https://blog.csdn.net/zghwaicsdn/article/details/50957474
tomcat:
http://www.cnblogs.com/kismetv/p/7228274.html
https://www.cnblogs.com/kismetv/p/7806063.html