Quick Start

构造参数解析

1
2
3
4
5
6
7
public ThreadPoolExecutor(int corePoolSize  // 核心线程数,
int maximumPoolSize // 最大线程数,
long keepAliveTime // 线程空闲后多久销毁,
TimeUnit unit // 时间单位,
BlockingQueue<Runnable> workQueue // 超过核心线程数后存储的队列,
ThreadFactory threadFactory // 自定义threadFactory,给线程命名等,
RejectedExecutionHandler handler // 拒绝策略)

拒绝策略是线程池的保护部分,线程池有一个最大的容量,当线程池的任务缓存队列已满,并且线程池中的线程数目达到maximumPoolSize时,就需要拒绝掉该任务,采取任务拒绝策略,保护线程池。

BlockingQueue队列:

mark

队列子类太多,大致过一遍就可以了,但是要记住基于链表的队列是有容量危险的,容易造成任务堆积和下方不允许使用Executors显示创建时一个原因。

两种创建方式

手动创建

1
2
3
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor
(1, 10, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(6), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

自动创建

juc包的executors类有提供默认的ThreadPoolExecutor类常用的几种实现

1
2
3
4
5
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}

阿里规范不允许显示使用

mark

执行过程

其执行过程如下:

  1. 首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。
  2. 如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。
  3. 如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。
  4. 如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
  5. 如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。

直接看文字有点抽象,直接看下面的demo代码,通过不同的线程池参数调节来观察

mark

demo1:

mark

核心线程数1,最大线程数1,linkedBlockingQueue

打印的线程name

mark

可以看到都是同一个线程在处理,所以处理逻辑,第一个任务进来,创建一个核心线程thread-1,因为最大核心线程数是1,所以后面的任务放到了linkedBlockingQueue里等待thread-1处理完成之后消费,一个个排队。

demo2

mark

linkedBlockingQueue容量为1,运行后

mark

可以看到只打印了线程执行就抛错了,所以逻辑应该是,第一个任务进来小于核心线程数,创建并执行。第二个任务进来,因为核心线程数为1,所以进入了linkedBlockingQueue。第三个任务进来,队列满了,最大线程数为1,不能创建了,只能抛出指定的拒绝策略。

demo3

mark

加大核心线程数,执行结果:

mark

与demo1一样,因为队列是无界的,所以触发不了队列满了之后创建线程。

demo4

mark

核心线程数1 + 最大线程数10 + 一个容量为100的队列 ,执行结果

mark

mark

mark

刚开始第一个任务进来,创建一个核心线程thread-1,后续的任务进入容量100的队列,101个任务开始因为最大线程数是10,开始创建thread2-thread10,同时这个10个线程开始并发的操作数据库修改user,同时user加了乐观锁导致修改失败,最后最大线程数10 + 队列容量100 = 110个满了之后,触发拒绝策略,所以可以看到并发开始了打印110次。