Java 线程池(ThreadPoolExecutor)原理分析与使用
来源:互联网 发布:ae2018cc破解版mac 编辑:程序博客网 时间:2024/05/20 15:47
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. * * <p>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. * * <p>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,创建一个线程的线程池 * <dl> *核心线程与最大线程篇 * <dt>Core and maximum pool sizes</dt> corePoolSize 核心线程数 maximumPoolSize 核心线程数 当一个任务提交到线程池 1) 如果当前线程池中的线程数小于corePoolSize时,直接创建一个先的线程。 2) 如果当前线程池中的线程数大于等于corePoolSize时,如果队列未满,直接将线程放入队列中,不新建线程。 3) 如果队列已满,但线程没有超过maximumPoolSize,则新建一个线程。 在运行过程中,可以通过调用setCorePoolSize,setMaximumPoolSize改变这两个参数 * * <dd>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}. </dd> * * * * <dt>On-demand construction</dt * 核心线程的创建通常是有任务提交时新建的,当然,我们可以通过调用prestartCoreThread,或 prestartAllCoreThreads方法,预先创建核心线程数。 * <dd> 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. </dd> * * <dt>Creating new threads</dt> *线程创建篇,新线程的创建,默认使用Executors.defautThreadFactory来创建线程,同一个线程创建工厂创建的线程具有相同的线程组,优先级,是否是后台线程(daemon),我们可以提供资金的线程创建工厂来改变这些属性,一般我们使用自己定义的线程工厂,主要的目的还是修改线程的名称,方便理解与跟踪。 * <dd>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.</dd> * * <dt>Keep-alive times</dt> * 如果线程池中线程数量超过了核心线程数,超过的线程如果空闲时间超过了keepAliveTime的线程会被终止; 先提出一个疑问:如果核心线程数设置为10,目前有12个线程,其中有3个超过了keepALiveTime,那有3个线程会被终止,还是只有两个,按照上述描述,应该是2个会被终止,,因为有个管家子 excess threads,从源码中去找答案吧。 * <dd>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. </dd> * * <dt>Queuing</dt> * * <dd>Any {@link BlockingQueue} may be used to transfer and hold * submitted tasks. The use of this queue interacts with pool sizing: * * <ul> * * <li> If fewer than corePoolSize threads are running, the Executor * always prefers adding a new thread * rather than queuing.</li> * * <li> If corePoolSize or more threads are running, the Executor * always prefers queuing a request rather than adding a new * thread.</li> * * <li> If a request cannot be queued, a new thread is created unless * this would exceed maximumPoolSize, in which case, the task will be * rejected.</li> * * </ul> * * There are three general strategies for queuing: 三种队列方案 1)直接传递,所有提交任务任务不入队列,直接传递给线程池。 2)有界队列 3)无界队列 采取何种队列,会对线程池中 核心线程数产生影响 再重复一下 核心线程的产生过程 1)如果当前线程池中线程数小于核心线程数,新任务到达,不管有没有队列,都是直接新建一个核心线程。 2)如果线程池中允许的线程达到核心线程数量时,根据不同的队列机制,有如下的处理方法: a、如果是直接传递,则直接新增线程运行(没有达到最大线程数量) b、如果是有界队列,先将任务入队列,如果任务队列已满,在线程数没有超过最大线程数限制的情况下,新 建一个线程来运行任务。 c、无界队列,则线程池中最大的线程数量等于核心线程数量,最大线程数量不会有产生任何影响。 * <ol> * * <li> <em> Direct handoffs.</em> 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. </li> * * <li><em> Unbounded queues.</em> 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. </li> * * <li><em>Bounded queues.</em> 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. </li> * * </ol> * * </dd> * * <dt>Rejected tasks</dt> * 任务拒绝策略 1)AbortPolicy,抛出运行时异常 2)CallerRunsPolicy 调用者直接运行,不在线程中运行。 3)DiscardPolicy 直接将任务丢弃 4)DiscardOldestPolicy 丢弃队列中头部的任务。 * <dd> New tasks submitted in method {@link #execute} will be * <em>rejected</em> 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: * * <ol> * * <li> In the default {@link ThreadPoolExecutor.AbortPolicy}, the * handler throws a runtime {@link RejectedExecutionException} upon * rejection. </li> * * <li> 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. </li> * * <li> In {@link ThreadPoolExecutor.DiscardPolicy}, a task that * cannot be executed is simply dropped. </li> * * <li>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.) </li> * * </ol> * * 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. </dd> * * <dt>Hook methods</dt> * * <dd>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. * * <p>If hook or callback methods throw exceptions, internal worker * threads may in turn fail and abruptly terminate.</dd> * * <dt>Queue maintenance</dt> * * <dd> 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.</dd> * * <dt>Finalization</dt> * * <dd> A pool that is no longer referenced in a program <em>AND</em> * 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)}. </dd> * * </dl> * * <p> <b>Extension example</b>. 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: * * <pre> {@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(); * } * } * }}</pre> * * @since 1.5 * @author Doug Lea */
2、ThreadPoolExecutors 内部数据结构与构造方法详解
ThreadPoolExecutors的完整构造函数如下,从构造函数中能得出线程池最核心的属性
- Java线程池(ThreadPoolExecutor)原理分析与使用
- Java 线程池(ThreadPoolExecutor)原理分析与使用 – 码农网
- Java 线程池(ThreadPoolExecutor)原理分析与使用
- JAVA线程池(ThreadPoolExecutor)原理分析与使用
- Java 线程池(ThreadPoolExecutor)原理分析与使用
- Java 线程池(ThreadPoolExecutor)原理分析与使用
- Java 线程池(ThreadPoolExecutor)原理分析与使用
- Java 线程池(ThreadPoolExecutor)原理分析与使用
- java线程池ThreadPoolExecutor原理及使用
- Java线程池ThreadPoolExecutor使用和分析
- Java ThreadPoolExecutor线程池原理及源码分析
- 也来分析下Java 线程池(ThreadPoolExecutor)的原理
- Java线程池ThreadPoolExecutor使用和分析(二)
- Java线程池ThreadPoolExecutor使用和分析(一)
- Java线程池ThreadPoolExecutor使用和分析(一)
- Java线程池ThreadPoolExecutor使用和分析(二)
- Java线程池ThreadPoolExecutor使用和分析(三)
- Java线程池ThreadPoolExecutor的execute()原理
- Codeforces Round #429 (Div. 2)
- 百练 2075: Tangled in Cables
- 汉诺塔
- 学习经验总结(二)
- 2017年8月19日训练日记
- Java 线程池(ThreadPoolExecutor)原理分析与使用
- JVM加载class文件的原理机制简单总结
- CCPC2017网络赛8月19日总结
- hdu-6153 A Secret
- 配置错误 在唯一密钥属性“fileExtension”设置为“.mp4”时,无法添加类型为“mimeMap”的重复集合项
- JavaScript实现下拉框联动的方法与见解
- 时间复杂度_函数传参、传引用、传指针
- mkdir()和mkdirs()的区别
- linux下创建和删除软、硬链接