线程池
一、什么是线程池
线程池就是提前创建若干个线程,如果有任务需要处理,线程池里的线程就会处理任务,处理完之后线程并不会被销毁,而是等待下一个任务。
二、为什么要有线程池?
1.减少在创建和销毁线程上所花的时间以及系统资源的开销
2.如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及”过度切换”(防止消耗过多内存)
三、在什么情况下使用线程池?
1.单个任务处理的时间比较短
2.将需处理的任务的数量大
四、ThreadPoolExecutor源码分析
(1)继承关系
//ThreadPoolExecutor继承了AbstractExecutorService类
public class ThreadPoolExecutor extends AbstractExecutorService{...}
(2)构造方法
//构造方法:
//用给定的初始参数和默认的线程工厂及被拒绝的执行处理程序创建新的 ThreadPoolExecutor。
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue)
//用给定的初始参数和默认的线程工厂创建新的 ThreadPoolExecutor。
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
// 用给定的初始参数和默认被拒绝的执行处理程序创建新的 ThreadPoolExecutor。
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
//用给定的初始参数创建新的 ThreadPoolExecutor。
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,RejectedExecutionHandler handler)
构造方法中各个参数的含义:
-
corePoolSize:池中所保存的线程数,包括空闲线程
-
maximumPoolSize:池中允许的最大线程数。这是池中最大能容纳的最大线程数量,如果超出,则使用RejectedExecutionHandler 拒绝策略处理。
-
keepAliveTime:当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。这里的生命周期有两个约束条件,一是该参数针对的是超过corePoolSize数量的线程。二是处于非运行状态的线程。
-
unit:参数keepAliveTime的时间单位。在TimeUnit中有7种取值,NANOSECONDS //纳秒, MICROSECONDS //微秒, MILLISECONDS //毫秒, SECONDS //秒, MINUTES //分钟, HOURS//小时, DAYS//天
-
workQueue:执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。
- 队列有三种形式:
1.SynchronousQueue 默认选项
2.LinkedBlockingQueue 无界队列。
3.ArrayBlockingQueue 有界队列。
- 队列有三种形式:
-
threadFactory:执行程序创建新线程时使用的工厂
-
handler:由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
- 有四种预定义的处理程序策略:
1.ThreadPoolExecutor.AbortPolicy (默认),处理程序遭到拒绝将抛出运行时 RejectedExecutionException。
2.ThreadPoolExecutor.CallerRunsPolicy ,线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。
3.ThreadPoolExecutor.DiscardPolicy ,不能执行的任务将被删除。
4.ThreadPoolExecutor.DiscardOldestPolicy ,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。
- 有四种预定义的处理程序策略:
(3)变量
private final BlockingQueue<Runnable> workQueue; //任务缓存队列,用来存放等待执行的任务
private final ReentrantLock mainLock = new ReentrantLock(); //线程池的主要状态锁,对线程池状态(比如线程池大小runState等)的改变都要 使用这个锁
private final HashSet<Worker> workers = new HashSet<Worker>(); //用来存放工作集
private volatile long keepAliveTime; //线程存货时间
private volatile boolean allowCoreThreadTimeOut; //是否允许为核心线程设置存活时间
private volatile int corePoolSize; //核心池的大小(即线程池中的线程数目大于这个参数时,提交的任务会被放进任 务缓存队列)
private volatile int maximumPoolSize; //线程池最大能容忍的线程数
private volatile int poolSize; //线程池中当前的线程数
private volatile RejectedExecutionHandler handler; //任务拒绝策略
private volatile ThreadFactory threadFactory; //线程工厂,用来创建线程
private int largestPoolSize; //用来记录线程池中曾经出现过的最大线程数
private long completedTaskCount;
(4)部分方法
void execute(Runnable command) //在将来某个时间执行给定任务
void shutdown() //按过去执行已提交任务的顺序发起一个有序的关闭,但是不接受新任务。如果已经关闭,则调用没有其他作用
List<Runnable> shutdownNow() //尝试停止所有的活动执行任务、暂停等待任务的处理,并返回等待执行的任务列表
boolean isShutdown() //如果此执行程序已关闭,则返回 true
1)ThreadPoolExecutor执行execute方法的四种情况:
- 如果当前运行的线程小于corePoolSize,则创建新线程来执行任务(执行这一步需要获取全局锁)
- 如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue
- 如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(执行这一步需要获取全局锁)
- 如果创建新线程将使当前运行的线程超出maxmumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()的方法
下面来看一下execute的代码:
public void execute(Runnable command) {
//首先,判断提交的任务command是否为null,为null,则抛出空指针异常;
if (command == null)
throw new NullPointerException();
//如果线程数小于基本线程数,则创建线程并执行当前任务
//如果线程数大于等于基本线程数或线程创建失败,则将当前任务放到工作队列中
if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
//如果当前线程池处于RUNNING状态,则将任务放入任务缓存队列
if (runState == RUNNING && workQueue.offer(command)) {
//如果关闭了线程池
if (runState != RUNNING || poolSize == 0)
//保证添加到任务缓存队列中的任务得到处理。
ensureQueuedTaskHandled(command);
}
//如果线程池不处于运行中或任务无法放入队列,并且当前线程数量小于最大允许的线程数量,则创建一个线程执行任务。
else if (!addIfUnderMaximumPoolSize(command))
reject(command); // is shutdown or saturated
}
}
参考资料:
1.https://blog.csdn.net/qq_36898043/article/details/79732932
2.http://tool.oschina.net/apidocs/apidoc?api=jdk-zh
3.https://blog.csdn.net/yingxiongtnt/article/details/83602228
4.《java并发编程的艺术》