1. 任务执行服务的基本概念
任务执行服务大大简化了执行异步任务所需的开发,它引入了一个“执行服务”的概念,将“任务的提交”和“任务的执行”相分离。“执行服务”封装了任务执行的细节,对于任务提交者而言,它可以关注于任务本身,如提交任务、获取结果、取消任务,而不需要关注任务执行的细节,如线程创建、任务调度、线程关闭等
任务执行服务主要涉及以下接口
Runnable
和Callable
:表示要执行的异步任务Executor
和ExecutorService
:表示执行服务Future
:表示异步任务的结果
使用者只需要通过
ExecutorService
提交任务,通过Future
操作任务和结果即可,不需要关注线程创建和协调的细节
2. 怎样理解线程池
- 任务执行服务的主要实现机制是线程池,实现类是
ThreadPoolExecutor
。线程池主要由两个概念组成:任务队列和工作者线程 - 任务队列是一个阻塞队列,保存待执行的任务,工作者线程主体就是一个循环,循环从队列中接收任务并执行。
TheadPoolExecutor
有一些重要的参数,理解这些参数对于合理使用线程池非常重要 ThreadPoolExecutor
实现了生产者/消费者模式,工作线程就是消费者,任务提交者就是生产者,线程池自己维护任务队列。当碰到类似生产者/消费者问题时,应该优先考虑直接使用线程池,而非“重新发明轮子”
3. 怎样理解定时任务
- 异步任务中,常见的任务是定时任务。在 Java 中,有两种方式实现定时任务
- 使用 java.util 包中的
Timer
和TimerTask
- 使用 Java 并发包中的
ScheduledExecutorService
- 使用 java.util 包中的
4. Timer
的需要特别注意的地方有
- 一个
Timer
对象背后只有一个Timer
线程。所以,定时任务不能太长,更不能是无限循环 - 在执行任何一个任务的
run()
方法时,一旦run()
抛出异常,Timer
线程就会退出,从而所有定时任务都会被取消
5. 怎样理解 ScheduledExecutorService
ScheduledExecutorService
的主要实现类是ScheduledThreadPoolExecutor
,它没有Timer
的问题- 它的背后是线程池,可以有多个线程执行任务
- 任务执行线程会捕获任务执行过程中的所有异常,一个定时任务的异常不会影响其他定时任务
实际开发中,当有定时任务时建议使用
ScheduledExecutorService
6. Java 中还有哪些并发相关的内容
- Java 7 引入的 Fork/Join 框架,Java 8 中有并行流的概念,可以让开发者非常方便地对大量数据进行并行操作,背后基于的就是 Fork/Join 框架
CompletionService
,在异步任务程序中,一种场景是:主线程提交多个异步任务,然后希望有任务完成就处理结果,并且按任务完成顺序逐个处理。对于这种场景,Java 并发包提供了一个方便的方法,那就是使用CompletionService
。CompletionService
是一个接口,它的实现类是ExecutorCompletionService
,它通过一个额外的结果队列,方便了对于多个异步任务结果的处理(细节可参考微信公众号“老马说编程”第 79 篇文章)- Java 8 引入组合式异步编程
CompletableFuture
,它可以方便地将多个有一定依赖关系的异步任务以流水线的方式组合在一起,自然地表达任务之间的依赖关系和执行流程,大大简化代码,提高可读性