四种自带线程池

403 阅读4分钟

一、概述

  • 腾讯面试的时候被问到了,总结一下

二、分类

1、CachedThreadPool

  • 它是一个可以无限扩大的线程池;

  • 它比较适合处理执行时间比较小的任务

  • corePoolSize为0,maximumPoolSize为无限大,意味着线程数量可以无限大;

  • keepAliveTime为60S,意味着线程空闲时间超过60S就会被杀死;

  • 采用SynchronousQueue装等待的任务,这个阻塞队列没有存储空间,这意味着只要有请求到来,就必须要找到一条工作线程处理他,如果当前没有空闲的线程,那么就会再创建一条新的线程。

public static ExecutorService newCachedThreadPool(){ 
    return new ThreadPoolExecutor(
    0, //  核心线程数
    Integer.MAX_VALUE, // 最大线程数
    60L,
    TimeUnit.MILLISECONDS,
    new SynchronousQueue<Runnable>()); 
 }

1、SingleThreadExecutor

public static ExecutorService newSingleThreadExecutor(){
    return new ThreadPoolExecutor(
    1,
    1,
    0L,
    TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>());
}
  • 它只会创建一条工作线程处理任务;
  • 采用的阻塞队列为LinkedBlockingQueue;

3、FixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads){
    return new ThreadPoolExecutor(
    nThreads,
    nThreads,
    0L,
    TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>());
    }
  • 它是一种固定大小的线程池;

  • corePoolSize和maximunPoolSize都为用户设定的线程数量nThreads;

  • keepAliveTime为0,意味着一旦有多余的空闲线程,就会被立即停止掉;但这里keepAliveTime无效;

  • 由于阻塞队列是一个无界队列,因此永远不可能拒绝任务;

  • 由于采用了无界队列,实际线程数量将永远维持在nThreads,因此maximumPoolSize和keepAliveTime将无效。

4、ScheduledThreadPool

  • 它用来处理延时任务或定时任务。

  • ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)

    • 创建并执行在给定延迟后启用的一次性操作
  • ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

    • 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;也就是将在initialDelay 后开始执行,然后在 initialDelay+period后执行,接着在initialDelay + 2 * period后执行,依此类推。
  • ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

    • 创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。
  • 区别

    • scheduleAtFixedRate 方法的执行周期都是固定的,也就是,他是以上一个任务的开始执行时间作为起点,加上之后的 period 时间,调度下次任务。

    • scheduleWithFixedDelay 方法则是以上一个任务的结束时间作为起点,加上之后的 period 时间,调度下次任务。

(1)参数

public ScheduledThreadPoolExecutor(int corePoolSize,
                                   ThreadFactory threadFactory,
                                   RejectedExecutionHandler handler) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue(), threadFactory, handler);
}
  • ScheduledThreadPoolExecutor最多支持 3 个参数:核心线程数量,线程工厂,拒绝策略

  • DelayedWorkQueue,是一个小根堆,是无界的,所以拒绝策略和最大线程数没啥用

(2)大致的执行流程

  • 首先判断当前任务是否是周期性任务。如果线程池当前运行状态下不能执行周期性任务,则取消任务的执行,否则执行步骤2;

  • 如果当前任务不是周期性任务,则直接调用FutureTask类的run方法执行任务,会设置执行结果,然后直接返回,否则执行步骤3;

  • 如果当前任务是周期性任务,则调用FutureTask类的runAndReset方法执行任务,不会设置执行结果,然后直接返回,否则执行步骤4;

  • 如果任务执行成功,则设置下次执行任务的时间,同时,将任务设置为重复执行

这里有个小细节就是,只有再任务执行成功后,才会设置下次的执行时间,如果任务失败,这个周期性任务就不会执行了,其次,当 scheduleAtFixedRate 类型(以任务开始时间作为周期),的任务如果执行时间过长,超出设置的定时频率时长,本次任务执行完才开始下次任务,下次任务已经处于超时状态,会马上开始执行.

(3)小结

  1. Rate是从上一个任务的开始执行时间开始计算;Delay 是从上一个任务的结束时间开始计算。

  2. 如果任务本身的时间超过了间隔时间,那么这两种模式的间隔时间将会不一致

  3. 而任务的排序是通过 ScheduledFutureTask的 compareTo方法排序的,规则是先比较执行时间,如果时间相同,再比较加入时间。

  4. 如果任务执行过程中异常了,那么将不会再次重复执行。因为 ScheduledFutureTask的 run方法没有做catch处理。

参考