Java线程池的实现原理和Executor框架

Java线程池

线程池的流程如下:

  1. 线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下个流程。
  2. 线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。
  3. 线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程(非核心线程池)来执行任务。如果已经满了,则交给饱和策略来处理这个任务。

实际上线程池分为两种线程,一种是核心线程池,另一种是非核心线程。核心线程数会一直在线程池中存活,即使处于闲置状态。非核心线程闲置时的超时时长超过keepAliveTime,则非核心线程就会被回收。

以下图来自Java并发编程的艺术,这两张图已经很好解释了线程池的主要处理流程和ThreadPoolExecutor执行流程

ThreadPoolExecutor详解

Executor框架最核心的类是ThreadPoolExecutor,它是线程池的实现类,由四个组件构成

  • corePool: 核心线程池的大小
  • maximumPool: 最大线程池的大小
  • BlockingQueue:用来暂时保存任务的工作队列
  • RejectedExecutionHandler: 当ThreadPoolExecutor已经关闭或ThreadPoolExecutor已经饱和时,execute()方法将要调用的Handler。

1. FixThreadPool

FixThreadPool被称为可重用固定线程数的线程池,应用于需要限制当前线程数量的应用场景,适用于负载比较重的服务器。

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

corePoolSize和maximumPoolSize都被设置为创建FixedThreadPool时指定的参数nThreads。

当线程池中的线程数达到corePoolSize后,新任务将在无界队列中等待,因此线程池中的线程数不会超过corePoolSize。使用无界队列时maximumPoolSize和keepAliveTime将是一个无效参数

2. SingleThreadExecutor

SingleThreadExecutor在线程池中有且只有一个核心线程,适用于需要保证顺序地执行各个任务。

public static ExecutorService newSingleThreadExecutor(){
    return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
}

corePoolSize和maximumPoolSize都被设置为1,其余参数与FixedTheadPool相同。

3. CachedThreadPool

CachedThreadPool是一个会根据需要创建新线程的线程池

public static ExecutorService newCachedThreadPool(){
    return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>());
}

corePoolSize设置为0,即核心线程为空,表明使用的都是非核心线程,maximumPoolSize设置为Integer.MAX_VALUE,即表示无界,keepAliveTime设置为60s,表示非核心线程等待新任务的最长时间为60s,超过60s,非核心线程将会被终止。SynchronousQueue是一个没有容量的阻塞队列,每个插入操作必须等待另一个线程的对应得移除操作,否则插入队列失败。

CachedThreadPool执行流程如下:

  1. 首先执行SynchronousQueue的插入,如果当前maximumPool中有非核心线程正在执行SynchronousQueue的移除,那么主线程执行的插入操作和非核心线程执行的移除操作配对成功,主线程把任务交给非核心线程执行,否则执行步骤2
  2. 当初始maximumPool为空,或者maximumPool中没有非核心线程时,无法执行SynchronousQueue的移除操作,这种情况下插入队列失败,此时CachedTheadPool会创建一个新线程执行任务。
  3. 在步骤2中创建的线程将任务执行完后,会执行SynchronousQueue的移除操作,这个移除操作会让非核心线程最多在SynchronousQueue中等待60s,如果60s内主线程提交了一个新任务,那么这个非核心线程将执行主线程提交的新任务,否则这个非核心线程将被终止回收。
Share