28.ThreadPoolExecutor

1.核心数据结构

ThreadPoolExecutor

//线程池状态和运行起来的线程数量
private final AtomicInteger ctl;
// 存放任务的阻塞队列
private final BlockingQueue workQueue;
// 对线程池内部各种变量进行互斥访问控制
private final ReentrantLock mainLock;
// 线程集合
private final HashSet workers;

Worker

private final class Worker extends AbstractQueuedSynchronizer implements Runnable { 
    // ... 
    final Thread thread; // Worker封装的线程 
    Runnable firstTask;   // Worker接收到的第1个任务,可以是null,如果是null,则线程从队列获取任务
    volatile long completedTasks; // Worker执行完毕的任务个数 
    // ... 
}

2.核心配置参数

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, 
BlockingQueue workQueue, ThreadFactory threadFactory)
  1. corePoolSize:在线程池中始终维护的线程个数。
  2. maxPoolSize:在corePooSize已满、队列也满的情况下,扩充线程至此值。
  3. keepAliveTime/TimeUnit:maxPoolSize 中的空闲线程,销毁所需要的时间,总线程数收缩
    回corePoolSize。
  4. blockingQueue:线程池所用的队列类型。
  5. threadFactory:线程创建工厂,可以自定义,有默认值
    Executors.defaultThreadFactory() 。 6. RejectedExecutionHandler:corePoolSize已满,队列已满maxPoolSize 已满,最后的拒绝策略。

3.线程池关闭


这里的状态迁移有一个非常关键的特征:从小到大迁移,-1,0,1,2,3,只会从小的状态值往大的状态值迁移,不会逆向迁移。

正确关闭线程池的步骤
关闭线程池的过程为:在调用 shutdown()或者shutdownNow()之后,线程池并不会立即关闭,接下来需要调用 awaitTermination() 来等待线程池关闭。

shutdown()与shutdownNow()的区别
1.shutdown()不会清空任务队列,会等所有任务执行完成,shutdownNow()清空任务队列。
2.shutdown()只会中断空闲的线程,shutdownNow()会中断所有线程。

4 任务的提交过程分析

  • 如果当前线程数小于corePoolSize,则启动新线程。添加Worker,并将command设置为Worker线程的第一个任务开始执行。
  • 如果当前的线程数大于或等于corePoolSize,则调用workQueue.offer放入队列。如果线程池正在停止,则将任务从队列移除,并拒绝任务请求。否则,放入队列中后发现没有线程执行任务,开启新线程
  • 线程数大于maxPoolSize,并且队列已满,调用拒绝策略

5.任务的执行过程分析

Woker的run()方法的实现过程

  • 使用给定的第一个任务并利用线程工厂创建Worker实例,调用Woker的run方法,再调用ThreadPoolExecutor的runWorker方法执行线程的运行
  • runWorker:中断Worker封装的线程,此时任务线程就可以开始运行了。获取线程锁,执行task.run()方法,执行任务中的代码。w.completedTasks++,线程已完成的任务数加1

6.线程池的4种拒绝策略

workqueue用完之后在execute(Runnable command)的最后,调用了reject(command)执行拒绝策略

  • 调用者直接在自己的线程里执行,线程池不处理,比如到医院打点滴,医院没地方了,到你家自己操作吧
  • 线程池抛异常
  • 线程池直接丢掉任务,神不知鬼不觉
  • 删除队列中最早的任务,将当前任务入队列

你可能感兴趣的:(28.ThreadPoolExecutor)