线程池(一)--基础篇

Image by Felix Merler from Pixabay

前言

在平时的开发中,经常遇到这样的东西,例如数据库连接池,web请求也是使用的池化技术,只是我们直接接触的比较少而已;

正文

什么是线程池

简单点理解就是装线程的一个池子,一种池化思想对一定数量的线程进行管理,如:线程创建,销毁,执行任务等;

线程池流程

  1. 提交任务至线程池
  2. 判断线程池的核心线程数是否已满,未满则创建核心线程来处理该任务,否则进入下一流程
  3. 判断线程池的队列数是否已满,未满则将该任务加入等待队列中,否则进入下一流程
  4. 判断线程池的最大线程数是否已满,未满则创建非核心线程处理任务,否则进入下一流程
  5. 根据线程池配置的拒绝策略对任务进行处理

流程图如下:

流程图

线程池

ThreadPoolExecutor

通过java中的ThreadPoolExecutor来创建线程池,参数如下:

1
2
3
4
5
6
7
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {}

参数说明:

  1. corePoolSize
    • 线程池中保留的线程数,即便线程是空闲的,除非设置allowCoreThreadTimeOut
    • 提交任务后,线程池中的线程才会开始创建,如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前把核心线程都创造好,并启动
  2. maximumPoolSize
    • 线程池允许创建的最大线程数量,当线程池配置的队列满了以后,会继续创建非核心线程,直到最大线程数量为止
  3. keepAliveTime
    • 当线程数大于核心线程数时,空闲(非核心)线程的存活时间,当超过这个时间后,空闲线程会被回收
  4. unit
    • keepAliveTime的时间单位
  5. workQueue
    • 工作队列,在任务执行前保存任务,该队列只保存由execute方法提交的Runnable任务
  6. threadFactory
    • 线程创建工厂,当执行创建新的线程时该工厂会被使用
    • 一般在此设置线程的名字(实现ThreadFactory接口的newThread方法)
  7. handler
    • 饱和策略,主要有四种
    • 当工作队列和线程数达到最大时,会使用该策略

4种拒绝策略:

  1. AbortPolicy:抛出异常,默认选项
  2. DiscardPolicy:直接丢弃任务
  3. DiscardOldestPolicy: 丢弃队列中最老的任务,将新任务保存
  4. CallerRunsPolicy:返回给调用线程处理该任务

自定义拒绝策略: 定义一个类,实现RejectedExecutionHandler接口的rejectedExecution方法就可以了

4种阻塞队列:

  1. ArrayBlockingQueue :基于数组结构的有界限阻塞队列,初始化是指定容量,先进先出
  2. LinkedBlockingQueue :基于链表结构的无界限阻塞队列,用此队列,那线程池就达不到最大线程数了
  3. SynchronousQueue : 同步阻塞队列;每次插入队列必须等待另一个线程的移除操作,反之亦然;
  4. PriorityBlockingQueue:优先级队列
  5. DelayedWorkQueue : 延迟队列,为ScheduledThreadPoolExecutor静态类
ThreadPoolExecutor运行状态

ThreadPoolExecutor有以下几种运行状态:

  1. RUNNING : 接收新的任务,并且处理队列中的任务
  2. SHUTDOWN :不接受新的任务,但是处理队列中的任务
  3. STOP :不接受新的任务,且不处理队列中的任务,并且中断正在执行中的任务
  4. TIDYING :所有的任务都结束,线程数量为零,线程池状态转为TIDYING将会运行terminated方法
  5. TERMINATED :terminated方法执行完毕

流程图如下: 状态流程图

创建线程的几种方式

以下的几种创建线程池的方式都使用的是ThreadPoolExecutor来创建线程池;

  1. Executors.newCachedThreadPool();
    源码如下:
    1
    2
    3
    4
    5
    public static ExecutorService newCachedThreadPool() {
         return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                       60L, TimeUnit.SECONDS,
                                       new SynchronousQueue<Runnable>());
     }
    
    • 核心线程为0
    • 最大线程数为Integer.MAX_VALUE
    • 空闲线程存活时间为60秒
    • 使用SynchronousQueue队列
  2. Executors.newFixedThreadPool(15);
    1
    2
    3
    4
    5
     public static ExecutorService newFixedThreadPool(int nThreads) {
         return new ThreadPoolExecutor(nThreads, nThreads,
                                       0L, TimeUnit.MILLISECONDS,
                                       new LinkedBlockingQueue<Runnable>());
     }
    
    • 核心线程和最大线程数相等
    • 空闲线程存活时间为0
    • 使用无界阻塞队列
  3. Executors.newScheduledThreadPool(1); 定时任务线程池 ``` java public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); }

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

1
2
3
4
5
6
7
8
9
10
11
12
13
- 核心线程数为corePoolSize,最大线程数为Integer.MAX_VALUE
- 空闲线程存活时间为0
- 使用内部延时队列DelayedWorkQueue

4. Executors.newSingleThreadExecutor();
   只有一个核心线程,且最大线程为核心线程
``` java
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
  • 核心线程数和最大线程数一致
  • 空闲线程存活时间为0
  • 使用无界阻塞队列LinkedBlockingQueue

线程池监控

线程池自带的方法:

方法

以上图中的方法可以查看线程池运行时的内部情况;除此之外,ThreadPoolExecutor内部还有三个方法可以去自定义:

  1. void beforeExecute(Thread t, Runnable r)
    该方法会在任务执行前执行

  2. void afterExecute(Runnable r, Throwable t)
    该方法在任务执行完后执行,即便是任务出错;

  3. void terminated()
    线程池终止是会执行该方法,例如调用shutdown(),shutdownNow(),remove方法等等;

最后

这篇文章只是对线程池的一些基本信息进行了说明;

参考

  1. Java中的线程池
  2. Java线程池分析
  3. Java线程池详解
  4. 深入理解 Java 线程池:ThreadPoolExecutor
  5. java高并发系列 - 第18天:JAVA线程池,这一篇就够了
  6. Java线程池实现原理及其在美团业务中的实践
  7. 面试必备:Java线程池解析
坚持原创技术分享,您的支持将鼓励我继续创作!