人的知识就好比一个圆圈,圆圈里面是已知的,圆圈外面是未知的。你知道得越多,圆圈也就越大,你不知道的也就越多。

0%

后端框架-异步调用

Spring 对异步调用提供了良好的支持,只需在启动类上添加注解 @EnableAsync,然后在要执行异步操作的类或方法上添加注解 @Async 即可。

开启异步调用

1
2
3
4
5
6
7
8
@EnableAsync
@SpringBootApplication
public class WebApplication {

public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
}

创建异步任务

@Async 注解既可以添加在类上,也可以添加在方法上,如果是添加在类上,则该类下的所有方法都将被异步执行。
如果异步方法有返回值,则需要指定其返回对象类型为 Future。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Slf4j
@Component
public class AsyncTask {

@Async
public void execute() {
log.info("Execution thread name: {}", Thread.currentThread().getName());

try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
log.error("Error!", e);
}
}

@Async
public Future<String> executeAndReturn() {
log.info("Execution thread name: {}", Thread.currentThread().getName());

try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
log.error("Error!", e);
}

return new AsyncResult<>("Hello world !");
}
}

需要注意的是,由于对于 Spring 默认使用代理模式处理 @Async,因此同一类中的本地调用不会被拦截,即 @Async 将会被忽略

调用异步任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Slf4j
@RequestMapping(("asyncCall"))
@RestController
public class AsyncCallController {
private final AsyncTask asyncTask;

public AsyncCallController(AsyncTask asyncTask) {
this.asyncTask = asyncTask;
}

@GetMapping("execute")
public void execute() {
log.info("Main thread name: {}", Thread.currentThread().getName());

asyncTask.execute();
}

@GetMapping("executeAndReturn")
public String executeAndReturn() throws ExecutionException, InterruptedException {
log.info("Main thread name: {}", Thread.currentThread().getName());

Future<String> future = asyncTask.executeAndReturn();
// 轮询,直到 future 中有值
while (true) {
if (future.isDone()) {
return future.get();
} else {
log.info("Task is working");
TimeUnit.SECONDS.sleep(1);
}
}
}

}

配置线程池

Spring 会调用由它管理的 Executor 来处理 @Async。如果没有配置 Executor,它会自己创建 SimpleAsyncTaskExecutor。我们可以通过配置参数来调整它的默认配置。

1
2
3
4
5
6
7
8
9
spring:
task:
execution:
pool:
core-size: 5
max-size: 10
queue-capacity: 200
keep-alive: 10s
thread-name-prefix: task-

我们也可以自定义 Executor,只需要实现类 AsyncConfigurer 并在其方法 getAsyncExecutor() 中返回自定义的 Executor。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Configuration
public class AsyncConfig implements AsyncConfigurer {
public static final String ASYNC_EXECUTOR_NAME = "taskExecutor";

private final TaskExecutionProperties properties;

public AsyncConfig(TaskExecutionProperties properties) {
this.properties = properties;
}

@Bean(ASYNC_EXECUTOR_NAME)
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(properties.getPool().getCoreSize());
executor.setMaxPoolSize(properties.getPool().getMaxSize());
executor.setQueueCapacity(properties.getPool().getQueueCapacity());
executor.setKeepAliveSeconds((int) properties.getPool().getKeepAlive().getSeconds());
executor.setThreadNamePrefix(properties.getThreadNamePrefix());
executor.setTaskDecorator(new ContextCopyingDecorator());
executor.initialize();
return executor;
}
}

如果需要同时配置多个线程池,可以配置多个 Executor Bean。(如果使用了 Spring MVC 中的异步请求,需求将 Executor 的类型设置为 AsyncTaskExecutor)

小礼物走一走,来 Github 关注我