车驰夜幕|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的七个参数有个深刻的认识 。 也很容易理解为什么使用无界队列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) {}}}}
- 小鹰说科技|美军警告:别想分割封锁我们,美火速增兵叙利亚!大批装甲车驰援
- 上观新闻|夜幕下的南京路步行街东拓段,灯光璀璨,景色更加迷人
- 柚子在野区|【当当与您早知道】月亮不“孤单”
- 车驰夜幕|美国GPS卫星精度0.4米,俄卫星的精度1.2米,中国北斗呢?
- 车驰夜幕|联合国向你发来活动邀请:与全球尖端人士探讨关注自然的真谛
- 车驰夜幕|简单聊聊处理器的核显
- 车驰夜幕|6年架构师带你学习微服务的注册与发现:服务发现的意义
- 车驰夜幕|开源软件分享-漂亮的WPF UI界面框架
- 车驰夜幕|回复头条网友的疑问:30系显卡性能提升这么大,为啥这么便宜
- 车驰夜幕|面试时被问到ThreadLocal别慌,你要的答案都在这里