ThreadPoolExecutor原理概述
在我们的开发中“池”的概念并不罕见,有数据库连接池、线程池、对象池、常量池等等。下面我们主要针对线程池来一步一步揭开线程池的面纱。
1、降低资源消耗
可以重复利用已创建的线程降低线程创建和销毁造成的消耗。
2、提高响应速度
当任务到达时,任务可以不需要等到线程创建就能立即执行。
3、提高线程的可管理性
线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控
首先我们看下当一个新的任务提交到线程池之后,线程池是如何处理的
1、线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则执行第二步。
2、线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里进行等待。如果工作队列满了,则执行第三步
3、线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务
这里提到了线程池的饱和策略,那我们就简单介绍下有哪些饱和策略:
AbortPolicy
为Java线程池默认的阻塞策略,不执行此任务,而且直接抛出一个运行时异常,切记ThreadPoolExecutor.execute需要try catch,否则程序会直接退出。
DiscardPolicy
直接抛弃,任务不执行,空方法
DiscardOldestPolicy
从队列里面抛弃head的一个任务,并再次execute 此task。
CallerRunsPolicy
在调用execute的线程里面执行此command,会阻塞入口
用户自定义拒绝策略(最常用)
实现RejectedExecutionHandler,并自己定义策略模式
下我们以ThreadPoolExecutor为例展示下线程池的工作流程图
1、如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。
2、如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。
3、如果无法将任务加入BlockingQueue(队列已满),则在非corePool中创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。
4、如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。
ThreadPoolExecutor采取上述步骤的总体设计思路,是为了在执行execute()方法时,尽可能地避免获取全局锁(那将会是一个严重的可伸缩瓶颈)。在ThreadPoolExecutor完成预热之后(当前运行的线程数大于等于corePoolSize),几乎所有的execute()方法调用都是执行步骤2,而步骤2不需要获取全局锁。
6)任务的丢弃策略
/**
* An {@link ExecutorService} that executes each submitted task using
* one of possibly several pooled threads, normally configured
* using {@link Executors} factory methods.
*
* Thread pools address two different problems: they usually
* provide improved performance when executing large numbers of
* asynchronous tasks, due to reduced per-task invocation overhead,
* and they provide a means of bounding and managing the resources,
* including threads, consumed when executing a collection of tasks.
* Each {@code ThreadPoolExecutor} also maintains some basic
* statistics, such as the number of completed tasks.
*
*
To be useful across a wide range of contexts, this class
* provides many adjustable parameters and extensibility
* hooks. However, programmers are urged to use the more convenient
* {@link Executors} factory methods {@link
* Executors#newCachedThreadPool} (unbounded thread pool, with
* automatic thread reclamation), {@link Executors#newFixedThreadPool}
* (fixed size thread pool) and {@link
* Executors#newSingleThreadExecutor} (single background thread), that
* preconfigure settings for the most common usage
* scenarios. Otherwise, use the following guide when manually
* configuring and tuning this class:
*
1、提供如下工厂方法创建线程池对象(ExecutorService实现类)
1)Executors.newCachedThreadPool,创建一个线程容量为Integer.MAX_VALUE的线程池,空闲时间为60s。
2)Executors.newFixedThreadPool,创建一个固定容量的线程池
3)Executors.newSingleThreadExecutor,创建一个线程的线程池
*
*核心线程与最大线程篇
* - Core and maximum pool sizes
corePoolSize 核心线程数
maximumPoolSize 核心线程数
当一个任务提交到线程池
1) 如果当前线程池中的线程数小于corePoolSize时,直接创建一个先的线程。
2) 如果当前线程池中的线程数大于等于corePoolSize时,如果队列未满,直接将线程放入队列中,不新建线程。
3) 如果队列已满,但线程没有超过maximumPoolSize,则新建一个线程。
在运行过程中,可以通过调用setCorePoolSize,setMaximumPoolSize改变这两个参数
*
* - A {@code ThreadPoolExecutor} will automatically adjust the
* pool size (see {@link #getPoolSize})
* according to the bounds set by
* corePoolSize (see {@link #getCorePoolSize}) and
* maximumPoolSize (see {@link #getMaximumPoolSize}).
*
* When a new task is submitted in method {@link #execute}, and fewer
* than corePoolSize threads are running, a new thread is created to
* handle the request, even if other worker threads are idle. If
* there are more than corePoolSize but less than maximumPoolSize
* threads running, a new thread will be created only if the queue is
* full. By setting corePoolSize and maximumPoolSize the same, you
* create a fixed-size thread pool. By setting maximumPoolSize to an
* essentially unbounded value such as {@code Integer.MAX_VALUE}, you
* allow the pool to accommodate an arbitrary number of concurrent
* tasks. Most typically, core and maximum pool sizes are set only
* upon construction, but they may also be changed dynamically using
* {@link #setCorePoolSize} and {@link #setMaximumPoolSize}.
*
*
*
* - On-demand construction
By default, even core threads are initially created and
* started only when new tasks arrive, but this can be overridden
* dynamically using method {@link #prestartCoreThread} or {@link
* #prestartAllCoreThreads}. You probably want to prestart threads if
* you construct the pool with a non-empty queue.
*
* - Creating new threads
*线程创建篇,新线程的创建,默认使用Executors.defautThreadFactory来创建线程,同一个线程创建工厂创建的线程具有相同的线程组,优先级,是否是后台线程(daemon),我们可以提供资金的线程创建工厂来改变这些属性,一般我们使用自己定义的线程工厂,主要的目的还是修改线程的名称,方便理解与跟踪。
* - New threads are created using a {@link ThreadFactory}. If not
* otherwise specified, a {@link Executors#defaultThreadFactory} is
* used, that creates threads to all be in the same {@link
* ThreadGroup} and with the same {@code NORM_PRIORITY} priority and
* non-daemon status. By supplying a different ThreadFactory, you can
* alter the thread's name, thread group, priority, daemon status,
* etc. If a {@code ThreadFactory} fails to create a thread when asked
* by returning null from {@code newThread}, the executor will
* continue, but might not be able to execute any tasks. Threads
* should possess the "modifyThread" {@code RuntimePermission}. If
* worker threads or other threads using the pool do not possess this
* permission, service may be degraded: configuration changes may not
* take effect in a timely manner, and a shutdown pool may remain in a
* state in which termination is possible but not completed.
*
* - Keep-alive times
* 如果线程池中线程数量超过了核心线程数,超过的线程如果空闲时间超过了keepAliveTime的线程会被终止;
先提出一个疑问:如果核心线程数设置为10,目前有12个线程,其中有3个超过了keepALiveTime,那有3个线程会被终止,还是只有两个,按照上述描述,应该是2个会被终止,,因为有个管家子 excess threads,从源码中去找答案吧。
* - If the pool currently has more than corePoolSize threads,
* excess threads will be terminated if they have been idle for more
* than the keepAliveTime (see {@link #getKeepAliveTime}). This
* provides a means of reducing resource consumption when the pool is
* not being actively used. If the pool becomes more active later, new
* threads will be constructed. This parameter can also be changed
* dynamically using method {@link #setKeepAliveTime}. Using a value
* of {@code Long.MAX_VALUE} {@link TimeUnit#NANOSECONDS} effectively
* disables idle threads from ever terminating prior to shut down. By
* default, the keep-alive policy applies only when there are more
* than corePoolSizeThreads. But method {@link
* #allowCoreThreadTimeOut(boolean)} can be used to apply this
* time-out policy to core threads as well, so long as the
* keepAliveTime value is non-zero.
*
* - Queuing
*
* - Any {@link BlockingQueue} may be used to transfer and hold
* submitted tasks. The use of this queue interacts with pool sizing:
*
*
*
* - If fewer than corePoolSize threads are running, the Executor
* always prefers adding a new thread
* rather than queuing.
*
* - If corePoolSize or more threads are running, the Executor
* always prefers queuing a request rather than adding a new
* thread.
*
* - If a request cannot be queued, a new thread is created unless
* this would exceed maximumPoolSize, in which case, the task will be
* rejected.
*
*
*
* There are three general strategies for queuing:
三种队列方案
1)直接传递,所有提交任务任务不入队列,直接传递给线程池。
2)有界队列
3)无界队列
采取何种队列,会对线程池中 核心线程数产生影响
再重复一下 核心线程的产生过程
1)如果当前线程池中线程数小于核心线程数,新任务到达,不管有没有队列,都是直接新建一个核心线程。
2)如果线程池中允许的线程达到核心线程数量时,根据不同的队列机制,有如下的处理方法:
a、如果是直接传递,则直接新增线程运行(没有达到最大线程数量)
b、如果是有界队列,先将任务入队列,如果任务队列已满,在线程数没有超过最大线程数限制的情况下,新
建一个线程来运行任务。
c、无界队列,则线程池中最大的线程数量等于核心线程数量,最大线程数量不会有产生任何影响。
*
*
* - Direct handoffs. A good default choice for a work
* queue is a {@link SynchronousQueue} that hands off tasks to threads
* without otherwise holding them. Here, an attempt to queue a task
* will fail if no threads are immediately available to run it, so a
* new thread will be constructed. This policy avoids lockups when
* handling sets of requests that might have internal dependencies.
* Direct handoffs generally require unbounded maximumPoolSizes to
* avoid rejection of new submitted tasks. This in turn admits the
* possibility of unbounded thread growth when commands continue to
* arrive on average faster than they can be processed.
*
* - Unbounded queues. Using an unbounded queue (for
* example a {@link LinkedBlockingQueue} without a predefined
* capacity) will cause new tasks to wait in the queue when all
* corePoolSize threads are busy. Thus, no more than corePoolSize
* threads will ever be created. (And the value of the maximumPoolSize
* therefore doesn't have any effect.) This may be appropriate when
* each task is completely independent of others, so tasks cannot
* affect each others execution; for example, in a web page server.
* While this style of queuing can be useful in smoothing out
* transient bursts of requests, it admits the possibility of
* unbounded work queue growth when commands continue to arrive on
* average faster than they can be processed.
*
* - Bounded queues. A bounded queue (for example, an
* {@link ArrayBlockingQueue}) helps prevent resource exhaustion when
* used with finite maximumPoolSizes, but can be more difficult to
* tune and control. Queue sizes and maximum pool sizes may be traded
* off for each other: Using large queues and small pools minimizes
* CPU usage, OS resources, and context-switching overhead, but can
* lead to artificially low throughput. If tasks frequently block (for
* example if they are I/O bound), a system may be able to schedule
* time for more threads than you otherwise allow. Use of small queues
* generally requires larger pool sizes, which keeps CPUs busier but
* may encounter unacceptable scheduling overhead, which also
* decreases throughput.
*
*
*
*
*
* - Rejected tasks
* 任务拒绝策略
1)AbortPolicy,抛出运行时异常
2)CallerRunsPolicy 调用者直接运行,不在线程中运行。
3)DiscardPolicy 直接将任务丢弃
4)DiscardOldestPolicy 丢弃队列中头部的任务。
* - New tasks submitted in method {@link #execute} will be
* rejected when the Executor has been shut down, and also
* when the Executor uses finite bounds for both maximum threads and
* work queue capacity, and is saturated. In either case, the {@code
* execute} method invokes the {@link
* RejectedExecutionHandler#rejectedExecution} method of its {@link
* RejectedExecutionHandler}. Four predefined handler policies are
* provided:
*
*
*
* - In the default {@link ThreadPoolExecutor.AbortPolicy}, the
* handler throws a runtime {@link RejectedExecutionException} upon
* rejection.
*
* - In {@link ThreadPoolExecutor.CallerRunsPolicy}, the thread
* that invokes {@code execute} itself runs the task. This provides a
* simple feedback control mechanism that will slow down the rate that
* new tasks are submitted.
*
* - In {@link ThreadPoolExecutor.DiscardPolicy}, a task that
* cannot be executed is simply dropped.
*
* - In {@link ThreadPoolExecutor.DiscardOldestPolicy}, if the
* executor is not shut down, the task at the head of the work queue
* is dropped, and then execution is retried (which can fail again,
* causing this to be repeated.)
*
*
*
* It is possible to define and use other kinds of {@link
* RejectedExecutionHandler} classes. Doing so requires some care
* especially when policies are designed to work only under particular
* capacity or queuing policies.
*
* - Hook methods
*
* - This class provides {@code protected} overridable {@link
* #beforeExecute} and {@link #afterExecute} methods that are called
* before and after execution of each task. These can be used to
* manipulate the execution environment; for example, reinitializing
* ThreadLocals, gathering statistics, or adding log
* entries. Additionally, method {@link #terminated} can be overridden
* to perform any special processing that needs to be done once the
* Executor has fully terminated.
*
*
If hook or callback methods throw exceptions, internal worker
* threads may in turn fail and abruptly terminate.
*
* - Queue maintenance
*
* - Method {@link #getQueue} allows access to the work queue for
* purposes of monitoring and debugging. Use of this method for any
* other purpose is strongly discouraged. Two supplied methods,
* {@link #remove} and {@link #purge} are available to assist in
* storage reclamation when large numbers of queued tasks become
* cancelled.
*
* - Finalization
*
* - A pool that is no longer referenced in a program AND
* has no remaining threads will be {@code shutdown} automatically. If
* you would like to ensure that unreferenced pools are reclaimed even
* if users forget to call {@link #shutdown}, then you must arrange
* that unused threads eventually die, by setting appropriate
* keep-alive times, using a lower bound of zero core threads and/or
* setting {@link #allowCoreThreadTimeOut(boolean)}.
*
*
*
* Extension example. Most extensions of this class
* override one or more of the protected hook methods. For example,
* here is a subclass that adds a simple pause/resume feature:
*
*
{@code
* class PausableThreadPoolExecutor extends ThreadPoolExecutor {
* private boolean isPaused;
* private ReentrantLock pauseLock = new ReentrantLock();
* private Condition unpaused = pauseLock.newCondition();
*
* public PausableThreadPoolExecutor(...) { super(...); }
*
* protected void beforeExecute(Thread t, Runnable r) {
* super.beforeExecute(t, r);
* pauseLock.lock();
* try {
* while (isPaused) unpaused.await();
* } catch (InterruptedException ie) {
* t.interrupt();
* } finally {
* pauseLock.unlock();
* }
* }
*
* public void pause() {
* pauseLock.lock();
* try {
* isPaused = true;
* } finally {
* pauseLock.unlock();
* }
* }
*
* public void resume() {
* pauseLock.lock();
* try {
* isPaused = false;
* unpaused.signalAll();
* } finally {
* pauseLock.unlock();
* }
* }
* }}
*
* @since 1.5
* @author Doug Lea
*/
ThreadPoolExecutors的完整构造函数如下,从构造函数中能得出线程池最核心的属性
Note: The {@link Executors} class includes a set of methods
After this method returns, subsequent calls to {@link #isDone} will