Java异步编程:第二部分


Executors

Java并发包中最重要的类之一是java.util.concurrent.Executors。它包含ExecutorService的Factory和Utility方法。让我们从执行程序服务如何管理并将任务分配给线程开始,稍后再讨论的工厂方法Executors。

ExecutorService

简而言之,Executor服务维护一个线程池并为其分配任务。ExecutorService中包含三个主要组件。

  1. ThreadFactory
  2. Blocking Queue
  3. ThreadPoolExecutor

executorservice.png

ThreadFactory

此类创建并按需提供线程。因此删除了样板代码,例如new Thread()。Executors默认情况下提供默认的线程工厂。

但是我们有类似CustomizableThreadFactoryspring的选项,它在创建的线程名称中添加了前缀文本。当我们使用多个线程和多个用例时,CustomizableThreadFactory 将大大有助于调试。(此外,这不仅是将前缀添加到线程名称的方法。)

Blocking Queue

BlockingQueue是线程安全队列,是java.util.concurrent程序包的一部分。它支持所有队列功能,并支持以下操作:在 检索 元素时等待队列变为非空 ,并在存储元素时等待队列中的空间变为可用。阻塞队列在设计时考虑了生产者-消费者模式。它还支持Collections接口。此处,程序代码会将任务添加到队列中(生产者),而执行者将接管并执行任务(消费者)。

线程池执行器

这是ExecutorService的核心部分。ThreadPoolExecutorExecutorService的实现。它需要ThreadFactory(或ExecutorsDefaultThreadFactory),Blocking queue和其他一些参数。

howexecutorworks-1.png

参见上图。让我将其分为三个步骤:

步骤0(初始化): 取决于ThreadPoolExecutorThreadPoolParameters的类型。它从线程工厂获取线程并保留线程池。 步骤1(提交): 在内部将任务(可运行或可调用)提交给ExecutorService时,它们会添加到“阻止队列”中。 步骤2(执行): ThreadPoolExecutor监视BlockingQueue;如果队列中有可用的任务,它将接收任务并将其分配给线程池中的线程。然后线程开始执行。可能会有一些极端情况。

  • 等待任务:当队列中没有可用任务时。
  • 等待线程:当前没有可分配的线程时。
  • 等待空间:当空间已满时,这会在大小有限的Queue中发生。这很少见;默认情况下,执行者将提供LinkedBlockingQueue,因为执行者仅担心任务的插入和删除。在这种情况下RejectedExecutionHandler将很有用。

ThreadPool参数

您可能已经看到我说了其他一些线程池参数。让我们讨论它们是什么以及它们如何控制线程池中的线程:

corePoolSize —保留在池中的线​​程数,即使它们处于空闲状态,除非设置了allowCoreThreadTimeOut maximumPoolSize —池中允许的最大线程数 keepAliveTime —当线程数大于内核数时,这是多余的空闲线程将在终止之前等待新任务的最长时间。 unit — keepAliveTime参数的时间单位 IllegalArgumentException 将在以下条件下抛出:

  • corePoolSize <0
  • keepAliveTime <0
  • maximumPoolSize <= 0
  • maximumPoolSize <corePoolSize

Factory Methods of Executors 我们始终可以自由地实现ExecutorServiceThreadPoolExecutor,但是Executor提供了很少的工厂方法来创建ExecutorService实例。

Executors.newFixedThreadPool(int nThreads)

创建一个重用固定数量线程的线程池。在任何时候,最多nThreads个线程都是活动的处理任务。 如果在所有线程都处于活动状态时提交了其他任务,则它们将在队列中等待,直到某个线程可用为止。

注意:如前一篇文章中所述,每个线程都与底层操作系统线程相关,并且与CPU内核数相关。作为最佳实践,建议使用所有可用的CPU内核以获得更好的性能。可以使用找到Runtime.getRuntime().availableProcessors()

Executors.newCachedThreadPool()

创建一个线程池,该线程池根据需要创建新线程,但是在可用之前重新使用以前构造的线程。 这些池通常将提高执行许多短暂的异步任务的程序的性能。 该线程池应谨慎使用。这不适用于长时间运行的任务。 默认情况下corePoolSize设置为0,将maximumPoolSize 设置为Integer.MAX_VALUE

Executors.newScheduledThreadPool(int corePoolSize)

这与CachedThreadPool非常相似,但是可以安排命令在给定的延迟后运行或定期执行。这将返回ScheduledExecutorService,这是ThreadPoolExecutor的扩展版本。 corePoolSize从method参数获取,并且maximumPoolSize 设置为Integer.MAX_VALUE

单线程执行器

Executors还提供了上述所有功能的单线程版本,该版本将仅在ThreadPool中容纳一个线程,这主要用于单元测试。

还有一种有趣的工厂方法是:

Executors.newWorkStealingPool

这是在Java 8中引入的,以利用工作窃取算法来实现最大的并行度。这将返回ForkJoinPool,并且它们的工作方式与ThreadPoolExecutor略有不同。我们将在以后的文章中看到它。

为了给您提供上述了解,将提交的每个任务按条件划分为多个子任务,然后执行并分组在一起以产生最终结果。

我认为今天我们走了很长一段路。让我们在这里暂停。下一篇文章将探讨如何将任务提交给ExecutorService以及它们的Future <T>


原文链接:http://codingdict.com