《Java核心技术卷》读书笔记-并发(十一)- 执行器和线程池

94 阅读4分钟

使用线程池的原因

  • 构建一个新的线程时有一定代价的。

  • 可以减少并发线程的数目。创建大量线程会大大降低性能甚至使虚拟机崩溃。

线程池工厂方法
方法描述
newCachedThreadPool必要时创建新线程,空闲时线程会被保留60秒
newFIxedThreadPool该池包含固定数量的线程;空闲线程会一直被保留
newSingleThreadExecutor只有一个线程的池,该线程顺序执行每一个提交的任务
newScheduledThreadPool用于预定执行而构建的固定线程池,替代java.util.Timer
newSingleThreadScheduledExecutor用于预定执行而构建的单线程池
newFIxedThreadPool

固定大小的线程池,超过的任务在队列中等待。

使用步骤
  1. 调用Executors类中静态的方法newCachedThreadPool或newFixedThreadPool。

  2. 调用submit提交Runnable或Callable对象。

  3. 如果想要取消一个任务,或如果提交Callable对象,就要保存好返回的Future对象

  4. 当不再提交任务时,调用shutdown。

预定执行

ScheduledExecutorService接口具有为预定执行或重复执行任务而设计的方法。由newScheduledThreadPool和newSingleThreadScheduledExecutor返回实现了该接口的对象。

其可以预定Runnable或Callable在初始的延迟之后只运行一次。也可以预定一个Runnable对象周期运行。

控制任务组的能力

执行器还可以控制一组相关任务。

  • ExecutorService#invokeAny方法,提交所有对象到一个Callable对象的集合,并返回某个已经完成了任务的结果。

  • ExecutorService#invokeAll方法,提交并返回所有结果,缺点是需要等待。

执行器完成服务ExecutorCompletionService可以按结果可获得顺序保存结果,而invokeAll可能因为第一个任务耗时太长而等待。相当于ExecutorCompletionService优化了任务执行顺序。

Fork-Join框架

适合处理计算密集型任务,可以由大到小分解子任务的任务。

使用框架完成类似的可分解子任务的递归计算时,需要提供一个扩展RecursiveTask(用于有返回)的类或者提供一个扩展RecursiveAction(用于没返回)的类,再覆盖compute方法来生成并调用子任务。

例:


class Counter extends RecursiveTask<Integer> {

. . .

protected Integer compute() {

if (to - from < THRESHOLD) {

// 实际处理,临界值设置

} else {

int mid = (from + to) / 2;

Counter first = new Counter(values, from, mid, filter);

Counter second = new Counter(values, mid, to, filter);

invokeAll(first, second); // 接收任务并阻塞直到完成

return first.join() + second.join(); //join用于生成结果

}

}

}

Fork-Join框架用了一种叫工作密取的方法平衡可用线程的工作负载。每个工作线程都有一个双端队列,一个工作线程将子任务压入队头(只有一个线程可以访问队头,因此不用加锁)。一个工作线程空闲时,会从另一个双端队列的队尾取一个任务。由于大的子任务都在队尾,所以密取很少出现。

CompletableFuture

CompletableFuture提供了“组合”的能力。可以将一个个异步任务组合起来。通过它,可以指定你希望做声明,以及希望以声明顺序执行这些工作。当然,这不会立即发生,不过重要的是代码都在一个地方。下面是它提供的一些功能。

下表的功能负责给一个CompletableFuture对象添加动作

方法参数描述
thenApplyT->U对结果应用一个函数
thenComposeT -> CompletableFuture对结果调用函数并执行返回的future
handle(T, Throwable) -> U处理结果或错误
thenAcceptT -> void类似thenApply,不过结果为void
whenComplete(T, Throwable) ->类似handle,结果为void
thenRunRunnable执行Runnable

下表的功能负责组合多个CompletableFuture对象

方法参数描述
thenCombineCompletableFuture,(T,U)->V执行两个动作并用给定函数组合结果
thenAcceptBothCompletableFuture,(T,U)->void与thenCombine类似,不过结果为void
runAfterBothCompletableFuture<?>,Runnable两个都完成后执行runnable
applyToEitherCompletableFuture,T->V得到其中一个的结果时,传入给定的函数
acceptEitherCompletableFuture,T->void与apply类似,不过结果为void
runAfterEitherCompletableFuture<?>,Runnable其中一个完成后执行runnable
static allOfCompletableFuture<?>...所有给定的future都完成后完成,结果为void
static anyOfCompletableFuture<?>...任意future完成后完成,结果为void