车驰夜幕|ThreadPoolExecutor线程池实现原理+源码解析( 三 )

这个构造方法有七个参数 , 如果能明白各个参数的作用 , 那么线程池的工作原理也就基本清晰了 。

  • int corePoolSize:核心线程数 , 当有新的任务提交到线程池时 , 会进行如下判断:线程池中线程数量小于corePoolSize时 , 会创建新线程处理任务 , 即使还有其他空闲的核心线程线程池中线程数量等于corePoolSize时 , 任务会加入到workQueue缓存队列 , 直到缓存队列满了 , 才会新建非核心线程去处理任务线程池中的线程数量等于maximumPoolSize且缓存队列已满时 , 会根据RejectedExecutionHandler参数指定的拒绝策略来处理提交的任务如果corePoolSize和maximumPoolSize相等 , 则创建的线程池大小是固定的 , 缓存队列满了就执行决绝策略
  • int maximumPoolSize:最大线程数
  • long keepAliveTime:非核心线程的最长空闲时间 , 超过了会被回收(allowCoreThreadTimeOut参数设置成true , 也会回收核心线程)
  • TimeUnit unit:keepAliveTime参数的单位
  • BlockingQueue workQueue:阻塞队列 , 用于缓存 , 保存正在等待执行的任务 。 一般有以下几种配置直接切换:常用的队列是SynchronousQueue无界队列:常用的队列是LinkedBlockingQueue , 队列基于链表实现 , 最大长度是Integer.MAX_VALUE , 虽然是有界的 , 但是值太大 , 所以认为是无界队列 。 使用无界队列可能会导致最大线程数maximumPoolSize失效 , 这点结合下文的线程池执行过程会很容易理解有界队列:常用的队列是ArrayBlockingQueue , 基于数组实现 , 能把最大线程数控制为maximumPoolSize 。 也能避免阻塞队列中堆积的任务过多 。
  • ThreadFactory threadFactory:线程Factory , 用来创建线程 。 使用默认的ThreadFactory创建的线程是具有相同优先级的非守护线程 。一般需要自定义ThreadFactory , 因为要给每个线程设置有意义的名称 。
  • RejectedExecutionHandler handler: 当线程数达到了最大线程数 , 且没有线程空闲 , 且缓冲队列也满了(也就是线程池饱和了) , 指定拒绝策略 , ThreadPoolExecutor自身提供了四种拒绝策略:AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常CallerRunsPolicy:利用调用者所在的线程执行任务 , 哪个线程提交这个任务 , 就由哪个线程执行DiscardOldestPolicy:丢弃缓存队列中头部的任务 , 重试提交的任务DiscardPolicy:直接丢弃显然默认的四种拒绝策略都不能很好的使用在生产环境 , 所以一般也需要自定义拒绝策略来处理饱和的任务 。 将暂时无法处理的任务存入中间件、数据库以及日志记录 。
线程池中线程的数量并不是越多越好 , 因为服务器的性能总是有限的 。 线程数过多会增加线程切换的开销 , 并且空闲线程的频繁回收也需要消耗资源 。 线程池的七个参数相辅相成 , 相互影响 , 设置的时候需要根据实际情况酌情考虑 。 看文字描述多少有些不清晰 , 如果能有张图的话就再好不过了 。 你就说巧不巧吧 , 刚好我画了一张图 。
车驰夜幕|ThreadPoolExecutor线程池实现原理+源码解析对照这张图和上面的描述 , 相信大家对ThreadPoolExecutor的七个参数有个深刻的认识 。 也很容易理解为什么使用无界队列LinkedBlockingQueue会使maximumPoolSize失效了 , 因为缓存队列可能永远不会满 。
核心方法毫无疑问 , 线程池最核心的方法除了构造方法 , 就是执行task的方法了 。 在看ThreadPoolExecutor的核心方法之前 , 先看一个非常非常重要的内部类Worker , 它是线程池中运行任务的最小单元 。
// 继承了AbstractQueuedSynchronizer , 是一把锁// 实现了Runnable接口 , 是一个线程执行的taskprivate final class Workerextends AbstractQueuedSynchronizerimplements Runnable{private static final long serialVersionUID = 6138294804551838833L;/** 运行任务的线程 */final Thread thread;/** 要运行的初始任务 , 可能为null */Runnable firstTask;/** 每个线程的任务计数器 */volatile long completedTasks;Worker(Runnable firstTask) {setState(-1); // inhibit interrupts until runWorkerthis.firstTask = firstTask;// 把自己作为一个任务传递给ThreadFactory创建的线程this.thread = getThreadFactory().newThread(this);}/** runWorker是一个非常重要的方法 , 后文详细介绍*/public void run() {runWorker(this);}// 值为0代表解锁状态// 值为1表示锁定状态protected boolean isHeldExclusively() {return getState() != 0;} // CAS的方式尝试加锁protected boolean tryAcquire(int unused) {if (compareAndSetState(0, 1)) {setExclusiveOwnerThread(Thread.currentThread());return true;}return false;} // 尝试释放锁protected boolean tryRelease(int unused) {setExclusiveOwnerThread(null);setState(0);return true;}public void lock(){ acquire(1); }public boolean tryLock(){ return tryAcquire(1); }public void unlock(){ release(1); }public boolean isLocked() { return isHeldExclusively(); }void interruptIfStarted() {Thread t;if (getState() >= 0} catch (SecurityException ignore) {}}}}