目录

一、线程池是什么?

二、线程池参数说明

三、线程池生命周期

四、四种常见线程池

总结

一、线程池是什么?

        线程池,是指管理一组工作线程的的资源池。线程池与任务队列密切相关,其中在任务队列workQueue中保存了所有等待运行的任务。 工作线程流程很简单:从任务队列获得一个任务,执行任务线程,然后返回线程池并等待下一个任务。线程池的优势有:

  • 复用已存在线程,分摊请求在建立线程及销毁线程时的cpu及内存开销;
  • 提高请求响应性,不会由于等待创建线程而延迟任务的执行;
  • 统一管理线程池。通过适当调整线程池参数,可以创建适当数量的线程以保持cpu的使用率,同时防止相互竞争资源而导致内存耗尽。 

二、线程池参数说明

         通常调用Executors静态方法会创建一个线程池,其中ThreadPoolExecutor类就是实现工作线程池的主类,同时也是顶层接口Excutor的实现类。看下ThreadPoolExecutor的参数都有哪些,此方面有4个构造函数,下面选了参数最多的一个:

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
                              long keepAliveTime, TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
  •  int corePoolSize:线程池中核⼼线程数。核⼼线程:线程池中有两类线程,核⼼线程和⾮核⼼线程。核⼼线程默认情况下会⼀直保留在线程池中,即使这个核⼼线程是空闲的,⽽⾮核⼼线程如果⻓时间的闲置,就会被销毁(临时⼯)。在java1.6中加了一个allowCoreThreadTimeOut方法,如果此值设为true,在keepAlive时间内没有到达任务,核心线程也会被销毁。
  • int maximumPoolSize:线程池中允许的最⼤线程数 。该值等于核⼼线程数量 + ⾮核⼼线程数量。
  • long keepAliveTime:⾮核⼼线程闲置存活时间。如果设置了allowCoreThreadTimeOut为true,同样适用于核心线程。⾮核⼼线程如果处于闲置状态超过该值,就会被销毁。
  • TimeUnit unit:keepAliveTime的单位。
  • BlockingQueue workQueue:阻塞队列,维护着等待执⾏的Runnable任务。基本的队列排队方法有3种:无界队列、有界队列和同步队列。下面是几个常用的队列:

   常⽤的⼏个阻塞队列:
          1. LinkedBlockingQueue
          无界队列,底层数据结构是链表,默认⼤⼩是 Integer.MAX_VALUE ,也可以指定⼤⼩。newFixedThreadPool和newSingleTreadExecutor默认使用该队列。如果任务快速到达,并且超过线程池的处理速度,那么队列将无限制增加,直到Integer.MAX_VALUE。
          2. ArrayBlockingQueue
          有界队列,底层数据结构是数组,需要指定队列的⼤⼩。有助于避免资源耗尽。
          3. SynchronousQueue
          同步队列,内部容量为0,每个put操作必须等待⼀个take操作,反之亦然。newCachedThreadPooll默认使用该队列。使用该队列可以避免任务排队,以便直接将任务移交给线程池处理。当线程池小于最大值时,threadPoolExecutor将创建新线程,否则根据拒绝策略拒绝任务。SynchronousQueue不是一个真正的队列,而是一种在线程间切换的机制。只有当线程池是无界的或者可以拒绝任务时,SynchronousQueue才有实际使用价值。
          4. DelayedWorkQueue
          延迟队列,该队列中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素 。newScheduledThreadPool默认使用该队列。

  • ThreadFactory threadFactory:创建线程的⼯⼚ ,⽤于创建线程。如果不指定,会新建⼀个默认的线程⼯⼚Executors.defaultThreadFactory()。DefaultThreadFactory创建的线程采用new Thread()方式,并统一设置了守护线程、线程优先级、线程名称、线程组等。
  • RejectedExecutionHandler handler:拒绝处理策略,线程数量⼤于最⼤线程数就会采⽤拒绝处理策略,四种拒绝处理的策略为 :

   1. ThreadPoolExecutor.AbortPolicy:默认拒绝处理策略,丢弃任务并抛 出RejectedExecutionException异常。
          2. ThreadPoolExecutor.DiscardPolicy:丢弃新来的任务,但是不抛出异常。
          3. ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列头部(最旧的)的任务,然后重新尝试执⾏程序(如果再次失败,重复此过程)。
          4. ThreadPoolExecutor.CallerRunsPolicy:由调⽤线程处理该任务。
 

三、线程池生命周期

//参考ThreadPoolExecutor.java类

    // ctl:可以看作一个int类型的数字,高3位表示线程池状态,低29位表示worker数量
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    // COUNT_BITS:Integer.SIZE=32,所以COUNT_BITS为29位
    private static final int COUNT_BITS = Integer.SIZE - 3;
    // CAPACITY:线程池允许的最大线程数。1左移29位,然后-1,即为2^29 -1
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
    
    // runState存储在高3位二进制中
    // 线程池5种状态,按大小排序如下:RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED 
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    // Packing and unpacking ctl
    // runStateOf: 获取线程池状态,通过按位与操作,低29位将全部变成0
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    // workerCountOf: 获取线程池worker数量,通过按位与操作,高3位将全部变成0
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    // ctlOf: 根据线程池状态和线程池worker数量,生成ctl值
    private static int ctlOf(int rs, int wc) { return rs | wc; }

        参考ThreadPoolExecutor.java文件,线程池有5种状态,如下:

  • RUNNING:接受新任务并处理排队的任务。
  • SHUTDOWN: 不接受新任务,但处理排队的任务,包括那些还未开始执行的任务。
  • STOP :不接受新任务,不处理排队的任务,中断正在进行的任务。
  • TIDYING: 所有任务都已终止,workerCount为零(ctl记录的任务数量为0),转换为TIDYING状态并将运行terminated()钩子方法。
  • TERMINATED:terminated() 方法完成后变为TERMINATED状态。

线程5种状态相互转换条件:

  •  RUNNING -> SHUTDOWN:调用shutdown()方法
  • (RUNNING or SHUTDOWN) -> STOP:调用shutdownNow() 方法,执行一个比较粗爆的关闭过程:尝试中断正在进行的任务,并且不再启动队列中尚未开始执行的任务。
  •  SHUTDOWN -> TIDYING:当任务队列及线程池都为空时
  • STOP -> TIDYING:当线程池为空时
  • TIDYING -> TERMINATED:执行terminated()钩子方法完成后

四、四种常见线程池

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

创建一个可缓存的线程池,适用于执行短期异步任务的场景。(可以使得任务快速得到执行,因为任务时间执行短,可以很快结束,线程池的规模不受控制。)。由于不创建核心线程池,也可以在60s后,回收空闲线程,新的线程可以复用,使线程池性能可伸缩。尝试将任务添加到SynchronousQueue队列, 如果SynchronousQueue已有任务在等待,⼊列操作将会阻塞。

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

创建一个固定大小的线程池,因为采用无界的阻塞队列,每当提交一个任务就创建新的线程池直到最大数量,这时实际线程数量永远不会变化。核⼼线程数量和总线程数量相等,都是传⼊的参数nThreads,所以只能创建核⼼线程,不能创建⾮核⼼线程。适用于负载较重的场景,对当前线程数量进行限制,保证线程数可控。

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

创建一个单线程的线程池,只有一个工作线程执行任务。由于采用LinkBlockingQueue,能够保证任务依照队列的顺序来执行(例如FIFO)。

  • newScheduledThreadPool
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1, threadFactory));
    }

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

适用于执行延时或者周期性任务,类似于Timer。 

总结

        以上就是线程池的基本原理。在实际工作中除了常见的四种线程池,也可用ThreadPoolExecutor构造自定义线程池。此文仅供参考。