ThreadPoolExecutor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}

构造方法参数解析

  1. corePoolSize:核心线程数

  2. maximumPoolSize:最大线程数,线程池允许创建的最大线程数

  3. workQueue:任务队列,BlockingQueue 接口的某个实现(常使用 ArrayBlockingQueueLinkedBlockingQueue

  4. keepAliveTime:空闲线程的保活时间,如果某线程的空闲时间超过这个值都没有任务给它做,那么可以被关闭了

    注意:这个值并不会对所有线程起作用;

    1. 如果线程池中的线程数少于等于核心线程数 corePoolSize,那么这些线程不会因为空闲太长时间而被关闭,
    2. 可以通过调用 allowCoreThreadTimeOut(true) 使核心线程数内的线程也可以被回收
  5. TimeUnit:参数的时间单位;TimeYnit 是枚举类

  6. threadFactory用于生成线程,一般我们可以用默认的就可以了

    通常,我们可以通过它将我们的线程的名字设置得比较可读一些

    Message-Thread-1Message-Thread-2 类似这样

  7. handler:当线程池已经满了,但是又有新的任务提交的时候,该采取什么策略由这个来指定

    rejectedExecutionHandler 用于处理当线程池不能执行此任务时的情况

    主要的策略有:(默认策略是 抛出异常)

    1. 抛出 RejectedExecutionException 异常
    2. 忽略任务
    3. 使用提交任务的线程来执行此任务
    4. 将队列中等待最久的任务删除,然后提交此任务

任务策略

​ 任务策略执行的时期是线程池的线程达到 maximumPoolSize 的时候,此时新提交的任务就会按照指定策略进行操作

  • CallerRunsPolicy:只要线程池没有被关闭,那么由提交任务的线程自己来执行这个任务
  • AbortPolicy:不管怎样,直接抛出 RejectedExecutionException 异常(默认)
  • DiscardPolicy:不做任何处理,直接忽略掉这个任务
  • DiscardOldestPolicy:如果线程池没有被关闭的话,把队列队头的任务(也就是等待了最长时间的)直接扔掉,然后提交这个任务到等待队列中

线程池中的线程创建时机

  1. 如果当前线程数少于 corePoolSize,那么提交任务的时候创建一个新的线程,并由这个线程执行这个任务
  2. 如果当前线程数已经达到 corePoolSize,那么将提交的任务添加到队列中等待线程池中的线程去队列中取任务
  3. 如果队列已满,那么创建新的线程来执行任务,需要保证池中的线程数不会超过 maximumPoolSize,如果此时线程数超过了 maximumPoolSize,那么执行拒绝策略

注意:如果将队列设置为无界队列,那么线程数达到 corePoolSize 后,其实线程数就不会再增长了

线程池的状态和状态间的转换

线程池状态

  • RUNNING(-1):这是正常的状态:接受新的任务,处理等待队列中的任务

  • SHUTDOWN(0):不接受新的任务提交,但是会继续处理等待队列中的任务

  • STOP(1):不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程

  • TIDYING(2):所有的任务都销毁了,workCount 为 0。线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated()

  • TERMINATED(3):terminated() 方法结束后,线程池的状态就会变成这个

线程池状态等于 0 的时候不能提交任务,大于 0 的话,连正在执行的任务也需要中断

状态间的切换

  • RUNNING -> SHUTDOWN:当调用了 shutdown() 后,会发生这个状态转换,这也是最重要的

  • (RUNNING or SHUTDOWN) -> STOP:当调用 shutdownNow() 后,会发生这个状态转换,这下要清楚 shutDown() 和 shutDownNow() 的区别了

  • SHUTDOWN -> TIDYING:当任务队列和线程池都清空后,会由 SHUTDOWN 转换为 TIDYING

  • STOP -> TIDYING:当任务队列清空后,发生这个转换

  • TIDYING -> TERMINATED:这个前面说了,当 terminated() 方法结束后

ThreadPoolExecutor总结

  1. corePoolSizemaximumPoolSize 之间的线程会被回收corePoolSize 的线程也可以通过设置而得到回收(allowCoreThreadTimeOut(true)

  2. workQueue 用于存放任务,添加任务的时候,如果当前线程数超过了 corePoolSize,那么往该队列中插入任务,线程池中的线程会负责到队列中拉取任务

  3. 如果某个任务执行出现异常,那么执行任务的线程会被关闭,而不是继续接收其他任务。然后会启动一个新的线程来代替它