第九章 Java异步编程
9.1 同步计算与异步计算
- 以异步方式执行的任务,称之为异步任务,其任务的发起与任务的执行是在不同的时间线上进行的。换而言之,任务的发起与任务的执行是并发的。同步是串行的。
- 同步任务的发起线程在其发起该任务之后必须等待该任务执行结束后才能够执行其他操作,这种等待往往意味着阻塞。
- 同步任务也并不一定总是会使其发起线程被阻塞,同步任务的发起线程也可能以轮询的方式来等待任务结束。所谓轮询(Polling)是指任务的发起线程不断地检查其发起地任务是否执行结束,若任务已执行结束则执行下一步操作,否则继续检查任务,直到该任务完成。阻塞意味着在同步任务执行结束前,该任务的发起线程并没有在运行(其生命周期状态不为runnable),而轮询意味着在同步任务执行结束前,该任务的发起线程仍然在运行。
- 多线程编程本质是异步的。
9.2 Java Executor框架
- Runnable接口和Callable接口都是对任务处理逻辑的抽象。Java.util.concurrent.Executor接口则是对任务的执行进行抽象。
void execute(Runnable comand)
- Executor接口使得任务的提交方(相当于生产者)只需要知道它调用Executor.execute方法便可以使指定的任务被执行,而无须关心任务具体的执行细节。可见,Executor接口使得任务的提交能够与任务执行的具体细节解耦。
-
对于同一个任务(Runnable实例),如果我们把它提交给一个ThreadPoolExecutor(它实现了Executor接口)执行,那么该任务就是异步执行;如果把这个任务提交给如下图所示的Executor实例执行,那么该任务就是同步执行。Executor接口一定程度上缩小了同步编程与异步编程的代码编写方式。
image.png
- Executor接口无法将任务的处理结果返回给客户端代码。ExecutorService接口继承自Executor接口。ExecutorService定义了几个submit方法,这些方法能够接受Callable接口或者Runnable接口表示的任务并返回相应的Future实例。
- CompletionService为异步任务的批量提交以及获取这些任务的处理结果提供了便利。
9.3 异步计算助手:FutureTask
- 无论是Runnable实例还是Callable实例所表示的任务,只要我们将其提交给线程池执行,那么这些任务就是异步任务。采用Runnable实例来表示异步任务,优点是任务既可以交给一个专门的工作者线程执行,也可以交给一个线程池或者Executor的其他实现类来执行;缺点是我们无法直接获取任务的执行结果。使用Callable可以获取接口,但是表示异步任务只能交给线程池执行。java.util.concurrent.FutureTask类融合了二者的优点。
9.4 任务计划
- 有些情况下,我们可能需要事先提交一个任务,这个任务并不是立即被执行的,而是要在指定的时间或者周期性地被执行,这种任务就被称为计划任务。典型地计划任务包括清理系统垃圾数据、系统监控、数据备份等。
网友评论