# 异步线程池
# 配置
在核心模块zc-web-core-spring-boot-starter
中,已经配置了@EnableAsync
,如果需要配置多个线程池用于不同的任务时,示例代码如下:
/**
* 线程池配置
*
* @author : quansheng.zhang
* @date : 2019/7/28 21:31
*/
@Configuration
public class ExecutorConfig {
@Bean("kafkaTaskExecutor")
@ConfigurationProperties("zc.executor")
public Executor kafkaTaskExecutor() {
return ExecutorMdcTaskBuilder.create().build();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
其中ExecutorMdcTaskBuilder (opens new window)为since4.3.0
,作用:ThreadPoolTaskExecutor
建造者,打印MDC路径的线程池任务的建造者。
属性配置
# 核心线程数:线程池创建时候初始化的线程数
zc.executor.core-pool-size=10
# 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
zc.executor.max-pool-size=20
# 缓冲队列:用来缓冲执行任务的队列
zc.executor.queue-capacity=2000
# 允许线程的空闲时间(秒):当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
zc.executor.keep-alive-seconds=60
# 设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住
zc.executor.await-termination-seconds=10
# 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
zc.executor.thread-name-prefix=default-executor-
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
线程拒绝策略:不在新线程中执行任务,而是有调用者所在的线程来执行 ThreadPoolExecutor.CallerRunsPolicy
# 使用
# @Async
@Async("kafkaTaskExecutor")
@Override
public void asyncToutiao() {
// 异步线程 日志打印
log.info("异步线程 日志打印开始");
try {
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("10s后,异步异常,日志打印结束");
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
特别提示
@Async使用注意事项
- 异步方法和调用异步方法的方法不能再同一个类
- 方法所属的类的对象需要是被Spring容器所管理的,也就是指被
@Controller
@Service
@Repository
@Component
这些注解的类
# ExecutorService
提供以下几种方法将任务提交到ExecutorService
对应的线程池:
// Executor 接口的方法
void execute(Runnable command);
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
常见面试题:`execute` 和 `submit` 的区别
execute
是Executor
接口的方法,而submit
是ExecutorService
的方法,并且ExecutorService
接口继承了Executor
接口。execute
只接受Runnable
参数,没有返回值;而submit
可以接受Runnable
参数和Callable
参数,并且返回了Future
对象,可以进行任务取消、获取任务结果、判断任务是否执行完毕/取消等操作。其中,submit
会对Runnable
或Callable
入参封装成RunnableFuture
对象,调用execute
方法并返回。- 通过
execute
方法提交的任务如果出现异常则直接抛出原异常,是在线程池中的线程中;而submit
方法是捕获了异常的,只有当调用Future
的get
方法时,才会抛出ExecutionException
异常,且是在调用get
方法的线程。
# execute(Runnable command)
executorService.execute(new Runnable() {
public void run() {
// todo something
}
});
//executorService.shutdown();
1
2
3
4
5
6
7
2
3
4
5
6
7
# submit(Runnable)
Future future = executorService.submit(new Runnable() {
public void run() {
// todo something
}
});
1
2
3
4
5
2
3
4
5
# submit(Callable)
Callable
的call()
方法可以返回结果
Future future = executorService.submit(new Callable(){
public Object call() throws Exception {
// todo something
return "Callable Result";
}
});
1
2
3
4
5
6
2
3
4
5
6