`

理解 ThreadPoolExecutor 线程池

阅读更多

一、构造方法及参数说明

java线程池的实现类是java.util.concurrent.ThreadPoolExecutor,常用构造方法:ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit,

BlockingQueue<Runnable>workQueue,RejectedExecutionHandler handler)

corePoolSize: 线程池维护线程的最少数量

maximumPoolSize:线程池维护线程的最大数量

keepAliveTime: 线程池维护线程所允许的空闲时间

unit: 线程池维护线程所允许的空闲时间的单位

workQueue: 线程池所使用的缓冲队列

handler: 线程池对拒绝任务的处理策略

二、ExecutorServiceExecutorsThreadPoolExecutor详解

ThreadPoolExcutor为一些Executor提供了基本的实现,这些Executor是由Executors中的工厂 newCahceThreadPool、newFixedThreadPool和newScheduledThreadExecutor返回的。 ThreadPoolExecutor是一个灵活的健壮的池实现,允许各种各样的用户定制

一个 ExecutorService,它使用可能的几个池线程之一执行每个提交的任务,通常使用 Executors 工厂方法配置。

线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行集合任务时使用的线程)的方法。每个 ThreadPoolExecutor 还维护着一些基本的统计数据,如完成的任务数。为了便于跨大量上下文使用,此类提供了很多可调整的参数和扩展挂钩。但是,强烈建议程序员使用较为方便的 Executors 工厂方法 

Executors.newCachedThreadPool无界线程池,工厂将最大池的大小设置为Integer.MAX_VALUE,核心池的大小设置为0,超时设置为一分钟。这样创建了无限扩大的线程池,会在需求量减少的情况下减少线程数量进行自动线程回收

Executors.newFixedThreadPool固定大小线程池工厂为请求的池设置了核心池的大小和最大池的大小,而且池永远不会超时

Executors.newSingleThreadExecutor单个线程的线程池

newFixedThreadPool和newSingleThreadExectuor默认使用的是一个无限的 LinkedBlockingQueue。如果所有的工作者线程都处于忙碌状态,任务会在队列中等候。如果任务持续快速到达,超过了它们被执行的速度,队列也会无限制地增加。稳妥的策略是使用有限队列,比如ArrayBlockingQueue或有限的LinkedBlockingQueue以及 PriorityBlockingQueue。

newCachedThreadPool使用SynchronousQueue队列,对于庞大或无限的池,可以使用SynchronousQueue,完全绕开队列,直接将任务由生产者交给工作者线程

另外,可以使用PriorityBlockingQueue通过优先级安排任务

它们均为大多数使用场景预定义了设置。否则,在手动配置和调整此类时,使用以下指导:

   核心和最大池大小

ThreadPoolExecutor 将根据 corePoolSize(参见 getCorePoolSize())和 maximumPoolSize(参见 getMaximumPoolSize())设置的边界自动调整池大小。

当新任务在方法 execute(java.lang.Runnable) 中提交时,如果运行的线程少于 corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。

如果运行的线程多于 corePoolSize 而少于 maximumPoolSize,则仅当队列满时才创建新线程。

如果设置的 corePoolSize 和 maximumPoolSize 相同,则创建了固定大小的线程池。

如果将 maximumPoolSize 设置为基本的无界值(如 Integer.MAX_VALUE),则允许池适应任意数量的并发任务。

在大多数情况下,核心和最大池大小仅基于构造来设置,不过也可以使用 setCorePoolSize(int) 和 setMaximumPoolSize(int) 进行动态更改。

   按需构造

默认情况下,即使核心线程最初只是在新任务需要时才创建和启动的,也可以使用方法 prestartCoreThread() 或 prestartAllCoreThreads() 对其进行动态重写。

   创建新线程

使用 ThreadFactory 创建新线程。如果没有另外说明,则在同一个 ThreadGroup 中一律使用 Executors.defaultThreadFactory() 创建线程,并且这些线程具有相同的 NORM_PRIORITY 优先级和非守护进程状态。通过提供不同的 ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态,等等。如果从 newThread 返回 null 时 ThreadFactory 未能创建线程,则执行程序将继续运行,但不能执行任何任务。

   保持活动时间

如果池中当前有多于 corePoolSize 的线程,则这些多出的线程在空闲时间超过 keepAliveTime 时将会终止(参见 getKeepAliveTime(java.util.concurrent.TimeUnit))。这提供了当池处于非活动状态时减少资源消耗的方法。如果池后来变得更为活动,则可以创建新的线程。也可以使用方法 setKeepAliveTime(long, java.util.concurrent.TimeUnit) 动态地更改此参数。使用 Long.MAX_VALUE TimeUnit.NANOSECONDS 的值在关闭前有效地从以前的终止状态禁用空闲线程。

   排队

    所有 BlockingQueue 都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:

l 如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。

l 如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。

l 如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。

   排队策略

l 直接提交

工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集合时出现锁定。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

l 无界队列

使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙的情况下将新任务加入队列。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

l 有界队列

当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。

   被拒绝的任务

当 Executor 已经关闭,并且 Executor 将有限边界用于最大线程和工作队列容量,且已经饱和时,在方法 execute(java.lang.Runnable) 中提交的新任务将被拒绝。在以上两种情况下,execute 方法都将调用其 RejectedExecutionHandler 的 RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) 方法。下面提供了四种预定义的处理程序策略:

l 在默认的 ThreadPoolExecutor.AbortPolicy 中,处理程序遭到拒绝将抛出运行时 RejectedExecutionException。

l 在 ThreadPoolExecutor.CallerRunsPolicy 中,线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。

l 在 ThreadPoolExecutor.DiscardPolicy 中,不能执行的任务将被删除。

l 在 ThreadPoolExecutor.DiscardOldestPolicy 中,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。

定义和使用其他种类的 RejectedExecutionHandler 类也是可能的,但这样做需要非常小心,尤其是当策略仅用于特定容量或排队策略时。

   挂钩方法

ThreadPoolExecutor类提供 protected 可重写的 beforeExecute(java.lang.Thread, java.lang.Runnable) 和 afterExecute(java.lang.Runnable, java.lang.Throwable) 方法,这两种方法分别在执行每个任务之前和之后调用。它们可用于操纵执行环境;例如,重新初始化 ThreadLocal、搜集统计信息或添加日志条目。此外,还可以重写方法 terminated() 来执行 Executor 完全终止后需要完成的所有特殊处理。

如果挂钩或回调方法抛出异常,则内部辅助线程将依次失败并突然终止。

   队列维护

方法 getQueue() 允许出于监控和调试目的而访问工作队列。强烈反对出于其他任何目的而使用此方法。remove(java.lang.Runnable) 和 purge() 这两种方法可用于在取消大量已排队任务时帮助进行存储回收。

分享到:
评论

相关推荐

    深入理解Java线程池:ThreadPoolExecutor _ Idea Buffer1

    1. RUNNING :能接受新提交的任务,并且也能处理阻塞队列中的任务 2. SHUTDOWN:关闭状态,不再接受新提交的任务,但却可以继续处理阻塞队列中已保

    使用线程池ThreadPoolExecutor 抓取论坛帖子列表

    在IT行业中,线程池是多线程编程中一个重要的概念,它可以帮助我们高效地管理和控制并发执行的任务。...通过深入理解线程池的工作机制和源码,我们可以更好地设计和优化我们的并发程序,实现高效的数据抓取。

    Java线程池与ThreadPoolExecutor.pdf

    Java线程池是Java并发编程中...总结来说,理解并正确使用Java线程池和ThreadPoolExecutor对于优化Java应用程序的并发性能至关重要。通过调整线程池的参数,可以平衡资源利用率和系统响应时间,从而提高整体的系统效率。

    线程池ThreadPoolExecutor原理源码分析.md

    ### 线程池 `ThreadPoolExecutor` 原理源码分析 #### 一、概述 线程池作为 Java 并发编程中的重要组件,在实际应用中被广泛使用。其核心类 `ThreadPoolExecutor` 实现了对线程的管理、调度等功能。本文将围绕 `...

    Tomcat线程池实现简介

    【标题】:深入理解Tomcat线程池机制 【描述】:Tomcat作为最流行的Java Web容器,其高效处理请求的能力离不开线程池的优化。本文将详细解析Tomcat线程池的实现原理,帮助读者深入理解其工作方式。 【标签】:Tomcat ...

    深入理解Java 线程池

    深入理解Java线程池 Java 线程池是 Java 编程中一个非常重要的概念,它可以帮助开发者更好地管理和控制线程的执行,提高系统的性能和效率。在 Java 中,线程池是通过 Executor 框架来实现的,Executor 框架提供了...

    线程池java

    ### 深入理解Java之线程池 #### 一、Java中的`ThreadPoolExecutor`类 在Java中,`ThreadPoolExecutor`是线程池的核心实现类之一,它提供了丰富的配置选项来满足不同的应用场景需求。该类继承自`...

    ThreadPoolExecutor运转机制介绍

    通过对构造函数参数的理解,我们可以精确地控制线程池的行为,从而更好地适应不同的应用需求。此外,通过了解 `newFixedThreadPool` 和 `newCachedThreadPool` 等预定义线程池的特点,我们可以更高效地利用系统资源...

    简单线程池与线程池检查的实现

    在标签“源码”和“工具”的提示下,我们可以推测该话题可能涉及到线程池的具体实现代码,例如使用Java的`java.util.concurrent.ThreadPoolExecutor`类进行线程池的创建和管理,以及可能通过自定义工具或框架对...

    Java版线程池实现

    Java线程池是一种高效管理并发任务执行的机制,它通过预先创建并维护一定数量的线程,从而避免了频繁地创建和销毁线程所带来的性能...然而,理解自定义线程池的工作原理可以帮助我们更好地理解和优化并发程序的执行。

    线程池源码

    在Java中,`java.util.concurrent` 包中的 `ExecutorService` 和其子类如 `ThreadPoolExecutor` 提供了线程池的实现。本项目源码是个人基于Java实现的线程池,旨在理解和掌握线程池的工作原理。 线程池的核心概念...

    java线程池和反射机制例子

    Java中,`java.util.concurrent`包下的`ExecutorService`接口和`ThreadPoolExecutor`类提供了线程池的相关功能。我们可以自定义线程池的大小、工作队列、拒绝策略等参数,以适应不同的并发需求。例如,通过`...

    线程池简单实现chenjiegui

    线程池是一种多线程处理形式,用于管理并发执行的任务,它可以提高系统资源的利用率,减少线程创建和销毁的时间开销,同时还能提供更好的任务...正确理解和使用线程池,对于提升Java应用的性能和健壮性具有重要意义。

    Java中的线程与线程池.pptx

    ThreadPoolExecutor是线程池的核心实现,其构造函数的参数定义了线程池的行为: - corePoolSize:核心线程数,即使无任务,这些线程也会一直存活。 - maximumPoolSize:线程池最大线程数,当工作队列满时,会创建新...

    java线程池封装j

    理解线程池的工作原理,根据业务需求选择合适的参数,以及正确处理拒绝策略,都是实现高效并发处理的关键。在实际开发中,应结合项目特点,进行细致的线程池设计和封装,以达到最佳的并发性能。

    java线程池概念.txt

     corePoolSize在很多地方被翻译成核心池大小,其实我的理解这个就是线程池的大小。举个简单的例子:  假如有一个工厂,工厂里面有10个工人,每个工人同时只能做一件任务。  因此只要当10个工人中有工人是空闲的...

    Android之线程池ThreadPoolExecutor的简介

    Android中的线程池ThreadPoolExecutor解决了单线程下载数据的效率慢和线程阻塞的的问题,它的应用也是优化实现的方式。所以它的重要性不言而喻,但是它的复杂性也大,理解上可能会有问题,不过作为安卓工程师,了解...

    Java 线程池的原理与实现

    理解线程池的工作原理有助于优化程序性能,避免过度创建和销毁线程带来的开销。 1. **线程池的基本概念** - **线程池管理器**:负责创建和管理线程池,包括初始化线程池、添加任务到线程池以及关闭线程池。 - **...

    一个简单的线程池例子

    线程池是多线程编程中的一个重要概念,它在Windows操作系统以及其他支持多线程操作系统的环境中广泛应用。线程池是一种管理线程...这有助于我们深入理解线程池的工作原理,从而更好地在实际项目中应用和优化线程池。

    线程池的简单实现

    线程池是多线程编程中的一个重要概念,它在服务器端程序中被广泛使用,以提高资源利用率和...通过分析`ThreadPoolManager`、`WorkThread`、`Task`以及`TestThreadPool`这些类,我们可以更好地理解和实现线程池的机制。

Global site tag (gtag.js) - Google Analytics