Executor框架(三) — 几种常见的线程池

一、概述

如下图所示,Executor框架中线程池的类型有如下2种:ThreadPoolExecutorScheduledThreadPoolExecutor

在Executors工具类中,提供了3种 ThreadPoolExecutor 类型的线程池。

  1. FixedThreadPool:一个可重用固定线程数的线程池
  2. SingleThreadExecutor:只有一个工作线程的线程池
  3. CachedThreadPool:是一个会根据需要创建新线程的线程池

在这里插入图片描述

二、ThreadPoolExecutor 线程池

线程池的参数含义请参考:Executor框架(二) — 线程池原理浅析 (线程池的创建)

2.1 FixedThreadPool

FixedThreadPool 是可重用固定线程数的线程池。

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

corePoolSizemaximumPoolSize 都被设置为指定的参数nThreads,意味着线程数固定。
keepAliveTime 设置为0L,意味着多余的空闲线程会被立即终止。
blockQueue 设置为 LinkedBlockingQueue 无界阻塞队列(LinkedBlockingQueues的默认容量为Integer.MAX_VALUE,可以认定为无界队列)。


在这里插入图片描述

FixedThreadPool.execute() 运行示意图如上图所示:

  1. 如果当前运行的线程数少于corePoolSize,则创建新线程来执行任务。
  2. 在线程池当前运行的线程数等于corePoolSize后,会将任务加入LinkedBlockingQueue(无界阻塞队列)。
  3. 线程执行完1中的任务后,会在循环中反复从 LinkedBlockingQueue 获取任务来执行。

使用无界队列 LinkedBlockingQueue 作为线程池的工作队列的影响。

  1. 由于是无界队列,当线程池中的线程数达到corePoolSize后,新任务可以一直插入无界队列中,队列不会满,所以不会创建新线程 (即:线程数不会超过corePoolSize)。
  2. 由于是无界队列,所以参数 maximumPoolSize 不会生效。
  3. 由于是无界队列,在未设置allowCoreThreadTimeOut=true的条件下,keepAliveTime参数不会生效 (没有非核心的线程)。
  4. 由于是无界队列,在未关闭线程池前,不会执行拒绝任务。

2.2 SingleThreadExecutor

SingleThreadExecutor是一个单线程的线程池。

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

在这里插入图片描述
SingleThreadExecutor.execute() 运行示意图如上图所示:

  1. 如果当前运行的线程数少于corePoolSize,则创建新线程来执行任务。
  2. 在线程池当前运行的线程数等于corePoolSize后,会将任务加入LinkedBlockingQueue(无界阻塞队列)。
  3. 线程执行完1中的任务后,会在循环中反复从 LinkedBlockingQueue 获取任务来执行。

SingleThreadExecutor 使用无界队列作为工作队列,对线程池带来的影响与FixedThreadPool相同。

2.3 CachedThreadPool

CachedThreadPool 根据需要创建新线程的线程池。

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

在这里插入图片描述

CachedThreadPool.execute() 运行示意图如上图所示:

  1. 首先执行SynchronousQueue.offer(Runnable task)。若当前maximumPool中有空闲线程正在执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS),那么主线程执行offer操作与空闲线程执行的poll操作配对成功,主线程把任务交给空闲线程执行,execute()方法执行完成;否则执行下面的步骤2。

  2. 当初始maximumPool为空,或者maximumPool中当前没有空闲线程时,将没有线程执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。这种情况下,步骤1将失败,此时CachedThreadPool会创建一个新线程执行任务,execute()方法执行完成。

  3. 在步骤2中新创建的线程将任务执行完后,会执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。这个poll操作会让空闲线程最多在SynchronousQueue中等待60秒钟。如果60秒钟内没有回去新任务,则该空闲线程将终止。反之,则继续执行任务。

注: 由于空闲60秒的空闲线程会被终止,因此长时间保持空闲的CachedThreadPool不会使用任何资源(线程都会被销毁)。

在这里插入图片描述

三、ScheduledThreadPoolExecutor 线程池

ScheduledThreadPoolExecutor 主要功能有两个:

/**
 * 达到给定的延时时间后,执行任务。这里传入的是实现Runnable接口的任务,
 * 因此通过ScheduledFuture.get()获取结果为null
 */
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);

/**
 * 达到给定的延时时间后,执行任务。这里传入的是实现Callable接口的任务,
 * 因此,返回的是任务的最终计算结果
 */
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);

/**
 * 以上一个任务开始的时间计时,period时间过去后,检测上一个任务是否执行完毕,
 * 如果上一个任务执行完毕,则当前任务立即执行,
 * 如果上一个任务没有执行完毕,则需要等上一个任务执行完毕后立即执行。
 */
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, 
											long initialDelay, 
											long period, 
											TimeUnit unit);

/**
 * 当达到延时时间initialDelay后,任务开始执行。
 * 上一个任务执行结束后到下一次任务执行,中间延时时间间隔为delay。
 * 以这种方式,周期性执行任务。
 */
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, 
												long initialDelay, 
												long delay, 
												TimeUnit unit);

3.1 ScheduledThreadPoolExecutor 运行机制

// Executors.class
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

// ScheduledThreadPoolExecutor.class
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
}

DelayedWorkQueue 是一个基于堆的数据结构(类似于DelayQueue和PriorityQueue),是一个无界队列。所以 maximumPoolSize=Integer.MAX_VALUE 在ScheduledThreadPoolExecutor 中不会生效。DelayedWorkQueue

在这里插入图片描述

ScheduledThreadPoolExecutor 的运行示意图如上图所示:
ScheduledThreadPoolExecutor的执行主要分为两大部分。

  1. 当调用 scheduleAtFixedRate() 方法或者 scheduleWithFixedDelay() 方法时,会向ScheduledThreadPoolExecutor 的 DelayedWorkQueue 添加一个实现了
    RunnableScheduledFutur接口的ScheduledFutureTask。
  2. 线程池中的线程从 DelayedWorkQueue 中获取 ScheduledFutureTask,然后执行任务。

为了实现周期性的执行任务,ScheduledThreadPoolExecutor 对 ThreadPoolExecutor做了如下修改。

  1. 使用 DelayedWorkQueue 作为任务队列。
  2. 获取任务的方式不同。
  3. 执行周期任务后,增加了额外的处理。

四、参考

《Java并发编程艺术》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值