Skip to content

Commit 7a2ce67

Browse files
authored
Create SpringBoot中实现异步调用.md
1 parent 0077b58 commit 7a2ce67

File tree

1 file changed

+169
-0
lines changed

1 file changed

+169
-0
lines changed
+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
```
2+
1.引入web依赖:
3+
<dependency>
4+
<groupId>org.springframework.boot</groupId>
5+
<artifactId>spring-boot-starter-web</artifactId>
6+
</dependency>
7+
2.开启异步支持: (入口类上加上@EnableAsync注解)
8+
@SpringBootApplication
9+
@EnableAsync
10+
public class DemoApplication {
11+
public static void main(String[] args) {
12+
SpringApplication.run(DemoApplication.class, args);
13+
}
14+
}
15+
3.编写支持异步调用的服务:
16+
@Service
17+
public class TestService {
18+
//底层默认使用logback
19+
private Logger logger = LoggerFactory.getLogger(this.getClass());
20+
@Async
21+
public void asyncMethod() {
22+
sleep();
23+
logger.info("异步方法内部线程名称:{}", Thread.currentThread().getName());
24+
}
25+
public void syncMethod() { sleep(); }
26+
private void sleep() {
27+
try {
28+
TimeUnit.SECONDS.sleep(2);
29+
} catch (InterruptedException e) {
30+
e.printStackTrace();
31+
}
32+
}
33+
}
34+
4.编写测试controller:
35+
@RestController
36+
public class TestController {
37+
private Logger logger = LoggerFactory.getLogger(this.getClass());
38+
@Autowired
39+
private TestService testService;
40+
@GetMapping("async")
41+
public void testAsync() {
42+
long start = System.currentTimeMillis();
43+
logger.info("异步方法开始");
44+
testService.asyncMethod();
45+
logger.info("异步方法结束");
46+
long end = System.currentTimeMillis();
47+
logger.info("总耗时:{} ms", end - start);
48+
}
49+
@GetMapping("sync")
50+
public void testSync() {
51+
long start = System.currentTimeMillis();
52+
logger.info("同步方法开始");
53+
testService.syncMethod();
54+
logger.info("同步方法结束");
55+
long end = System.currentTimeMillis();
56+
logger.info("总耗时:{} ms", end - start);
57+
}
58+
}
59+
5.启动项目,分别测试同步方法和异步方法的调用:
60+
同步: http://localhost:8080/sync
61+
同步方法开始
62+
同步方法结束
63+
总耗时:2004 ms
64+
异步: http://localhost:8080/async
65+
异步方法开始
66+
异步方法结束
67+
总耗时:4 ms
68+
异步方法内部线程名称:task-1
69+
(当遇到异步方法时,会新启一个线程来执行异步方法)
70+
(默认情况下的异步线程池配置使得线程不能被重用,每次调用异步方法都会新建一个线程)
71+
6.自定义异步线程池:
72+
[1]自定义线程池的配置(使用ThreadPoolTaskExecutor的方法):
73+
corePoolSize: 线程池核心线程的数量,默认值为1;(默认异步线程池配置使得线程不能被重用的原因)
74+
maxPoolSize: 线程池维护的线程的最大数量,默认值为Integer.MAX_VALUE;
75+
只有当核心线程都被用完并且缓冲队列满后,才会开始申超过请核心线程数的线程;
76+
queueCapacity: 缓冲队列;
77+
keepAliveSeconds: 超出核心线程数外的线程在空闲时候的最大存活时间,默认为60秒;
78+
threadNamePrefix: 线程名前缀;
79+
waitForTasksToCompleteOnShutdown: 是否等待所有线程执行完毕才关闭线程池,默认值为false;
80+
awaitTerminationSeconds: waitForTasksToCompleteOnShutdown的等待的时长,默认值为0,即不等待;
81+
rejectedExecutionHandler: 当没有线程可以被使用时的处理策略(拒绝任务),默认策略为abortPolicy;
82+
包含下面四种策略:
83+
callerRunsPolicy: 用于被拒绝任务的处理程序,它直接在execute方法的调用线程中运行被拒绝的任务;
84+
如果执行程序已关闭,则会丢弃该任务;
85+
abortPolicy: 直接抛出java.util.concurrent.RejectedExecutionException异常;
86+
discardOldestPolicy: 当线程池中的数量等于最大线程数时,抛弃线程池中最后一个要执行的任务,
87+
并执行新传入的任务;
88+
discardPolicy: 当线程池中的数量等于最大线程数时,不做任何动作;
89+
[2]添加自定义异步线程:
90+
@Configuration
91+
public class AsyncPoolConfig {
92+
@Bean
93+
public ThreadPoolTaskExecutor asyncThreadPoolTaskExecutor(){
94+
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
95+
executor.setCorePoolSize(20);
96+
executor.setMaxPoolSize(200);
97+
executor.setQueueCapacity(25);
98+
executor.setKeepAliveSeconds(200);
99+
executor.setThreadNamePrefix("asyncThread");
100+
executor.setWaitForTasksToCompleteOnShutdown(true);
101+
executor.setAwaitTerminationSeconds(60);
102+
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
103+
executor.initialize();
104+
return executor;
105+
}
106+
}
107+
[3]使用自定义异步线程池时,@Async注解需指定线程池Bean名称:
108+
@Service
109+
public class TestService {
110+
......
111+
@Async("asyncThreadPoolTaskExecutor")
112+
public void asyncMethod() {
113+
......
114+
}
115+
......
116+
}
117+
7.处理异步回调:
118+
[1]Future及其实现类的关系:
119+
Future(接口) --> ListenableFuture(接口) --> AsyncResult(类,Spring实现的Future实现类)
120+
@Async("asyncThreadPoolTaskExecutor")
121+
public Future<String> asyncMethod() {
122+
sleep();
123+
logger.info("异步方法内部线程名称:{}", Thread.currentThread().getName());
124+
return new AsyncResult<>("hello async");
125+
}
126+
[2]异步回调的具体使用:
127+
(1)在service的异步方法中使用Future包装需要返回的对象:
128+
@Service
129+
public class TestReturnService {
130+
//底层默认使用logback
131+
private Logger logger = LoggerFactory.getLogger(this.getClass());
132+
@Async("asyncThreadPoolTaskExecutor")
133+
public Future<String> asyncReturnMethod(){
134+
sleep();
135+
logger.info("异步方法内部线程名称:{}", Thread.currentThread().getName());
136+
return new AsyncResult<>("hello async");
137+
}
138+
private void sleep() {
139+
try {
140+
TimeUnit.SECONDS.sleep(2);
141+
} catch (InterruptedException e) {
142+
e.printStackTrace();
143+
}
144+
}
145+
}
146+
(2)在调用异步方法的地方,使用Future接口的get方法获取异步方法真实返回的对象:
147+
@RestController
148+
public class TestController {
149+
private Logger logger = LoggerFactory.getLogger(this.getClass());
150+
@Autowired
151+
private TestService testService;
152+
@Autowired
153+
private TestReturnService testReturnService;
154+
@GetMapping("asyncReturn")
155+
public String testAsyncReturn() throws ExecutionException, InterruptedException {
156+
long start = System.currentTimeMillis();
157+
logger.info("异步方法开始");
158+
Future<String> stringFuture = testReturnService.asyncReturnMethod();
159+
String result = stringFuture.get();
160+
logger.info("异步方法返回值:{}", result);
161+
logger.info("异步方法结束");
162+
long end = System.currentTimeMillis();
163+
logger.info("总耗时:{} ms", end - start);
164+
return result;
165+
}
166+
(Future的get方法为阻塞方法,只有当异步方法返回内容了,程序才会继续往下执行;)
167+
(get(long timeout, TimeUnit unit)重载方法可设置超时时间,即异步方法在设定时间内没有返回值的话,
168+
直接抛出java.util.concurrent.TimeoutException异常)
169+
```

0 commit comments

Comments
 (0)