第9章 Java中的线程池
线程池的好处:
1.降低资源消耗:减少线程创建和销毁,重复利用线程
2.提高响应速度:线程提前创建,接收到任务线程立即执行
3.提高线程的可管理性:线程池可以统一分配、调优和监控
9.1 线程池的实现原理
当接收到一个新任务时,线程池工作流程如下:
1.线程池判断核心线程池里的线程是否都在执行任务,如果不是,创建一个新的工作线程来执行任务,如果是,下一步;
2.线程池判断工作队列是否已满,如果不是,则将新任务存储到工作队列,如果是,下一步;
3.线程池判断线程池的线程是否都处于工作状态,如果不是,则创建一个新的工作线程来执行任务,如果是,交给饱和策略来处理这个任务。
原书此处表达不准确,看execute()方法源码注释
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
1.如果少于核心线程池大小的线程正在运行,则开启一个新的线程,用提交的任务作为该线程的第一个任务。
调用addWorker方法会原子地检查运行状态和工作线程数量,从而防止不应该增加线程而返回false。
2.如果一个任务能够成功的加入队列,我们仍需要再次检查是否应该添加一个线程(因为存在自从上一次检查已经有线程消亡的情况)还是自从进入这个方法线程池已经关闭。
所以我们重新检查状态,如果有必要,状态停止则回滚入队操作,没有线程则创建一个新的线程。
3.如果不能将任务加入队列,那么我们尝试创建一个新的线程。如果创建失败表示线程池已关闭或者饱和,所以拒绝了任务
9.2 线程池的使用
9.2.1 线程池的创建
看构造方法源码注释
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit timeUnit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectionExecutionHandler handler){
...
}
corePoolSize 线程池线程的保有量,即使其他线程空闲
maximumPoolSize 线程池允许的线程数量的最大限度
keepAliveTime 当线程数量大于核心数量,超量部分的等待新任务的空闲线程终止前的最大时间限度
unit keepAliveTime的时间单位
workQueue 用来持有执行前任务的队列.这个队列只会存储由execute方法提交的Runnable任务
threadFactory executor创建一个新的线程时使用的工厂
handler 饱和策略,当execution因为线程界限和队列容量达到而阻塞时使用
workQueue种类:
ArrayBlockingQueue 基于数组结构的阻塞队列
LinkedBlockingQueue 基于链表结构的阻塞队列,吞吐量比ArrayBlockingQueue高
SynchronousBlockingQueue 不存储元素的阻塞队列,添加操作会一直等待取出操作,吞吐量高于LinkedBlockingQueue
PriorityBlockingQueue 具有优先级别的无限阻塞队列
9.2.2 向线程池提交任务
execute() 提交不需要返回值的任务
submit() 提交需要返回值的任务,返回一个Future对象,调用Future的get()方法可以获取返回结果,但会阻塞当前线程直到任务执行完成
9.2.3 关闭线程池
通过调用shutdown和shutdownNow方法来关闭线程池,其原理是遍历线程池中的所有线程,调用线程的interrupt方法来中断线程
shutdownNow方法会先将线程池状态置为STOP,尝试停止所有正在执行或者暂停任务的线程,并返回等待执行任务的列表
shutdown方法会将线程池状态置为SHUTDOWN,然后中断所有没有正在执行任务的线程
9.2.4 合理配置线程池
从以下几个角度分析
任务性质:CPU密集型任务——配置(CPU个数+1)个线程,IO密集型任务——配置(CPU个数*2)个线程,混合型任务——分解任务看吞吐量,两个任务执行时间相差太大则没必要分解
Runtime.getRuntime().availableProcessors() 获取当前设备的CPU个数
任务的优先级:使用PriorityBlockingQueue队列让优先级高的任务先执行,注意:如果一直有优先级高的任务在队列里,优先级低的任务可能永远不被执行
任务的执行时间:使用PriorityBlockingQueue让执行时间短的任务先执行
任务的依赖性:依赖数据库连接的任务等待时间长,线程数应该设置得更大以便更好地利用CPU
建议使用有界队列,使用无界队列如果执行任务出现问题线程阻塞,任务队列又不断被加入任务导致内存撑满,整个系统崩溃
9.2.5 线程池的监控
taskCount 线程池需要执行的任务数量
completedTaskCount 已完成的任务数量
largestPoolSize 线程池曾创建过的最大线程数量
getPoolSize 线程池的线程数量
getActiveCount 线程池活动的线程数量