线程池底层原理你了解吗。。。-CSDN博客

30 阅读6分钟

🌈🌈🌈🌈🌈🌈🌈🌈
添加微信好友(通过文章导读添加)一起交流学习,也可以找我拿一下整篇文章PDF!
【11来了】文章导读地址:点击查看文章导读!
🍁🍁🍁🍁🍁🍁🍁🍁

线程池到底怎么运行的?

简单介绍一下线程池

首先,线程池是将多个线程进行池化操作,统一进行管理,这样做有什么好处呢?

  • 降低创建、销毁线程的开销 :线程池中维护固定数量的线程,不需要临时进行线程的创建和销毁
  • 提高响应速度 :对于新提交到线程池中的任务,直接使用线程池中的空闲线程可以直接进行处理,不需要等待创建线程
  • 节省资源:可以重复利用线程

线程池中重要的参数

线程池中重要的参数如下:

  • corePoolSize :核心线程数量

  • maximumPoolSize :线程池最大线程数量 = 非核心线程数+核心线程数

  • keepAliveTime :非核心线程存活时间

  • unit:空闲线程存活时间单位(keepAliveTime单位)

  • workQueue :工作队列(任务队列),存放等待执行的任务

    • LinkedBlockingQueue:无界的阻塞队列,最大长度为 Integer.MAX_VALUE
    • ArrayBlockingQueue:基于数组的有界阻塞队列,按FIFO排序
    • SynchronousQueue:同步队列,不存储元素,对于提交的任务,如果有空闲线程,则使用空闲线程来处理;否则新建一个线程来处理任务
    • PriorityBlockingQueue:具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
  • threadFactory :线程工厂,创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。

  • handler: 拒绝策略 ,有4种

    • AbortPolicy :直接抛出异常,默认策略
    • CallerRunsPolicy:用调用者所在的线程来执行任务
    • DiscardOldestPolicy:丢弃阻塞队列里最老的任务,也就是队列里靠前的任务
    • DiscardPolicy :当前任务直接丢弃

新加入一个任务,线程池如何进行处理呢?

新加入一个任务,线程池处理流程如下:

  1. 如果核心线程数量未达到,创建核心线程执行
  2. 如果当前运行线程数量已经达到核心线程数量,查看任务队列是否已满
  3. 如果任务队列未满,将任务放到任务队列
  4. 如果任务队列已满,看最大线程数是否达到,如果未达到,就新建非核心线程处理
  5. 如果当前运行线程数量未达到最大线程数,则创建非核心线程执行
  6. 如果当前运行线程数量达到最大线程数,根据拒绝策略处理

在这里插入图片描述

如何将任务提交到线程池中呢?

有两种方式:executesubmit

这两种方式的区别:

  • execute

    • execute 没有返回值
    • execute 无法捕获任务过程中的异常
  • submit

    • submit 会返回一个 Future 对象,用来获取任务的执行结果
    • submit 可以通过 Future 对象来捕获任务中的异常

execute 方式如下:

ExecutorService executor = Executors.newFixedThreadPool(5);
executor.execute(new Runnable() {
    public void run() {
        // 执行具体的任务逻辑
        System.out.println("Task executed using execute method");
    }
});
executor.shutdown();

submit 方式如下:

ExecutorService executor = Executors.newFixedThreadPool(5);
Future<String> future = executor.submit(new Callable<String>() {
    public String call() {
        // 执行具体的任务逻辑
        return "Task executed using submit method";
    }
});

try {
    String result = future.get(); // 获取任务执行结果
    System.out.println(result);
} catch (InterruptedException e) {
    // 处理中断异常
} catch (ExecutionException e) {
    // 处理任务执行异常
} finally {
  // 关闭线程池
  executor.shutdown();
}

线程池是如何关闭的呢?

通过调用线程池的 shutdown() 方法即可关闭线程池

调用之后,会设置一个标志位表示当前线程池已经关闭,会禁止向线程池中提交新的任务

去中断所有的空闲线程并且等待正在执行的任务执行完毕(通过调用线程 interrupt() 方法),当线程池中所有任务都执行完毕之后,线程池就会被完全关闭

扩展:thread.interrupt() 方法调用后线程会立即中断吗?

不会,调用 interrupt 只是将被中断线程的中断状态设置为 true,通知被中断的线程自己处理中断,而不是立即强制的让线程直接中断(强制中断不安全)

当外部调用线程进行中断的命令时,如果该线程处于被阻塞的状态,如 Thread.sleep(),Object.wait(),BlockingQueue#put,BlockingQueue#take 等等时,那么此时调用该线程的 interrupt 方法就会抛出 InterruptedException 异常

线程池参数如何设置呢?

线程池参数的设置主要有两个:

  1. 线程池中 线程数量 的设置
  2. 任务队列 的设置

线程数量的设置:

一般情况下线程数量根据是 CPU 密集还是 IO 密集进行设置即可

对于 核心线程数量 的设置:

  • 对于 CPU 密集型任务,将核心线程数量设置为 cpu 核心核心数量 + 1 即可
  • 对于 IO 密集型任务,将核心线程数量设置为 cpu 核心数量的两倍

对于 最大线程数量 的设置:

  • 最大线程数量设置为核心线程数量一样即可
  • 对于 IO 密集型任务,可以将最大线程数量设置的再多一些,避免因为 IO 执行时间过长,导致大量任务阻塞

任务队列的设置:

尽量使用有界队列,避免大量任务进入队列中等待,占用大量内存,并且线程数量会一直保持在核心线程数量,不会创建新的线程去处理任务,增加任务响应时间

具体一点的话,可以根据每秒需要执行的任务数量,以及每个任务执行的时间来设置 核心线程数量

比如 tasks = 500,taskCost = 0.1s,系统容忍最大响应时间 responseTime = 1s,那么每秒就需要执行 500 个任务,每个任务花费 0.1s,那么核心线程数量需要设置为 500 / (1 / taskCost) = 500 * 0.1 = 50,需要 50 个核心线程每秒执行 50 个任务才可以保证在 1s 内将 500 个任务执行完毕

最大线程数量的设置可以根据 (最大任务数 - 队列容量) / 每个线程每秒处理任务数量 来设置

阿里手中的册线程池规范

在使用线程池的时候,需要注意一些规范,以免出现不必要的问题,可以参考阿里巴巴 Java 开发手册,如下:

线程池名称命名规范:

在这里插入图片描述

线程池创建规范:

在这里插入图片描述