开启异步调用
在主函数里面加上@EnableAsync
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
使用默认异步调用
在接口上加@Async
,那么此接口就是异步的了
@Service
public class TestService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Async
public void asyncMethod() {
sleep();
logger.info("异步方法内部线程名称:{}", Thread.currentThread().getName());
}
public void syncMethod() {
sleep();
}
private void sleep() {
try {
//让当前线程阻塞2秒钟
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
查看效果:
@RestController
public class TestController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private TestService testService;
@GetMapping("async")
public void testAsync() {
long start = System.currentTimeMillis();
logger.info("异步方法开始");
testService.asyncMethod();
logger.info("异步方法结束");
long end = System.currentTimeMillis();
logger.info("总耗时:{} ms", end - start);
}
@GetMapping("sync")
public void testSync() {
long start = System.currentTimeMillis();
logger.info("同步方法开始");
testService.syncMethod();
logger.info("同步方法结束");
long end = System.currentTimeMillis();
logger.info("总耗时:{} ms", end - start);
}
}
这样可以看出这两个接口之间的差异,因为被@Async
标注后的方法是异步执行的,所以他不会等待阻塞!
自定义线程池
定义线程池我所知的有两个简便方法一个是用Executors
工具类来快速定义,但是在《阿里巴巴开发手册》里这种做法被明令禁止,他们用ThreadPoolExecutor
来创建线程池。而在SpringBoot里也有封装好的线程池ThreadPoolTaskExecutor
,你只需要自己手动配置下就可以了,过程大致类似ThreadPoolExecutor
。
@Configuration
public class AsyncPoolConfig {
@Bean
public ThreadPoolTaskExecutor asyncThreadPoolTaskExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(200);
executor.setQueueCapacity(25);
executor.setKeepAliveSeconds(200);
executor.setThreadNamePrefix("Async-Thread-");
//是否等待所有线程执行完毕才关闭线程池,默认值为false。
executor.setWaitForTasksToCompleteOnShutdown(true);
// awaitTerminationSeconds:waitForTasksToCompleteOnShutdown的等待的时长,默认值为0,即不等待。
executor.setAwaitTerminationSeconds(60);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
要使用该线程池,只需要在@Async注解上指定线程池Bean名称即可:
package com.example.demo.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@Service
public class TestService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Async("asyncThreadPoolTaskExecutor")
public void asyncMethod() {
sleep();
logger.info("异步方法内部线程名称:{}", Thread.currentThread().getName());
}
public void syncMethod() {
sleep();
}
private void sleep() {
try {
TimeUnit.SECONDS.sleep(2);
logger.info("线程:{} 休眠结束!",Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程池的配置其实还是比较讲究的,要结合自己的项目和实际环境来配置!
处理回调
有点基础的小伙伴都知道在多线程里面想要有返回值,我们要实现Callable
接口,Future
来接收回调值
@Async("asyncThreadPoolTaskExecutor")
public Future<String> asyncMethod() {
sleep();
logger.info("异步方法内部线程名称:{}", Thread.currentThread().getName());
return new AsyncResult<>("hello async");
}
控制器:
@GetMapping("async")
public String testAsync() throws Exception {
long start = System.currentTimeMillis();
logger.info("异步方法开始");
Future<String> stringFuture = testService.asyncMethod();
String result = stringFuture.get();
logger.info("异步方法返回值:{}", result);
logger.info("异步方法结束");
long end = System.currentTimeMillis();
logger.info("总耗时:{} ms", end - start);
return stringFuture.get();
}
这次我们会发现,似乎比上面的同步方法慢了,因为Future
的get
方法为阻塞方法,只有当异步方法返回内容了,程序才会继续往下执行。
深度了解请点击
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/16416.html