线程池(重点)

文章介绍了线程池的概念,包括其如何维护线程、常用的线程池类如FixedThreadPool、CachedThreadPool、SingleThreadExecutor和ScheduledThreadPool的工作机制,以及线程池的关键参数如核心线程数、最大线程数、存活时间和工作队列。此外,文章还讨论了线程池的执行流程和拒绝策略,并建议避免直接使用Executors工具类创建线程池,而应自定义ThreadPoolExecutor以获得更好的灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

本文章介绍常用的线程池


一、什么是线程池?

线程池内部维护了若干个线程,没有任务的时候,这些线程都处于等待空闲状态。如果有新的线程任务,就分配一个空闲线程执行。如果所有线程都处于忙碌状态,线程池会创建一个新线程进行处理或者放入队列(工作队列)中等待。

二、线程池常用类和接口

在Java标准库提供了如下几个类或接口,来操作并使用线程池:

  • ExecutorService接口:进行线程池的操作访问;
  • Executors类:创建线程池的工具类;
  • ThreadPoolExecutor及其子类:封装线程池的核心参数和运行机制;

// 线程池基本使用方式
// 创建一个ThreadPoolExecutor类型的对象,代表固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(3); // 该线程池拥有3个线程
// 执行线程任务
executor.execute(task1);
executor.execute(task2);
executor.execute(task3);
executor.execute(task4);
executor.execute(task5);
// 使用结束后,使用shutdown关闭线程池
executor.shutdown();

三、线程池的常见方法

  • 执行无返回值的线程任务:void execute(Runnable command);
  • 提交有返回值的线程任务:Future submit(Callable task);
  • 关闭线程池:void shutdown();shutdownNow();
  • 等待线程池关闭:boolean awaitTermination(long timeout, TimeUnit unit);

四、线程池的执行流程

  1. 提交一个新的线程任务,线程池会在线程池中分配一个空闲线程(ps:不会一次性创建指定的核心线程数,而是来一个任务创建一个新的空闲的核心线程,到核心线程数饱和为止),用于执行任务。
  2. 如果线程池中不存在空闲线程,线程池会判断当前“存活的线程数”是否小于核心线程数
    • 如果 小于 核心线程数corePoolSize,线程池会创建一个新的核心线程去处理新的线程任务
    • 如果 大于 核心线程数corePoolSize,线程池会检查工作队列是否已满
      • 如果工作队列未满,则将该线程任务加入工作队列进行等待。线程池中如果出现空闲的线程,将从工作队列中按照先进先出规则取出一个线程任务进行分配执行;
      • 如果工作队列已满,则判断当前存活的线程数是否达到最大线程数maximumPoolSize;
        • 如果当前“存活的线程数”没有达到最大线程数maximumPoolSize,那么将创建一个临时的非核心线程执行新线程任务;
        • 如果当前“存活的线程数”已经达到最大线程数maximumPoolSize,那么将执行拒绝策略处理新线程任务;
          综上所述,执行顺序为:核心线程、工作队列、非核心线程、拒绝策略

在这里插入图片描述

五、线程池的参数

  • corePoolSize线程池核心线程数:也可以理解为线程池维护的最小线程数量,核心线程创建后不会被回收。大于核心线程数的线程,在空闲时间超过keepAliveTime后会被回收;
    • 在创建了线程池后,默认情况下,线程池中并没有任何线程,当调用 execute() 方法添加一个任务时,如果正在运行的线程数量小于corePoolSize,则马上创建新线程并运行这个任务。
    • IO密集计算:由于 I/O 设备的速度相对于 CPU来说都很慢,所以大部分情况下,I/O 操作执行的时间相对于 CPU 计算来说都非常长,这种场景我们一般都称为 I/O 密集型计算。最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)]。
    • CPU密集型:CPU 密集型计算大部分场景下都是纯 CPU 计算,多线程主要目的是提升CPU利用率,最佳线程数 =“CPU 核心数 +1”。这样的话,当线程因为偶尔的内存页失效或其他原因导致阻塞时,这个额外的线程可以临时替补,从而保证 CPU 的利用率。
  • maximumPoolSize线程池最大线程数:线程池允许创建的最大线程数量;(包含核心线程池数量)keepAliveTime非核心线程线程存活时间:当一个可被回收的线程的空闲时间大于keepAliveTime,就会被回收。
    • 当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会被回收,直到线程池中的线程数不超过corePoolSize。
    • 如果设置allowCoreThreadTimeOut = true,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
  • TimeUnit时间单位:参数keepAliveTime的时间单位;
  • BlockingQueue阻塞工作队列:用来存储等待执行的任务;
  • ThreadFactory线程工厂 : 用于创建线程,以及自定义线程名称,需要实现ThreadFactory接口;
  • RejectedExecutionHandler拒绝策略:当线程池线程内的线程耗尽,并且工作队列达到已满时,新提交的任务,将使用拒绝策略进行处理;
    • ThreadPoolExecutor.AbortPolicy:默认策略,丢弃任务并抛出RejectedExecutionException异常;
    • ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常;
    • ThreadPoolExecutor.DiscardOldestPolicy:丢弃工作队列中的队头任务(即最旧的任务,也就是最早进入队列的任务)后,继续将当前任务提交给线程池;
    • ThreadPoolExecutor.CallerRunsPolicy:由原调用线程处理该任务 (谁调用,谁处理);

六、常见的线程池

6.1 FixedThreadPool

线程数固定的线程池

  • 线程池参数
    • 核心线程数和最大线程数一致
    • 非核心线程存活时间为0
    • 阻塞队列为无界队列LinkedBlockingQueue
  • 工作机制
    • 提交线程任务
      • 如果线程数少于核心线程,创建核心线程执行任务
      • 如果线程数等于核心线程,把任务添加到LinkedBlockingQueue阻塞队列
      • 如果线程执行完任务,去阻塞队列取任务,继续执行
  • 使用场景: 适用于处理CPU密集型的任务,确保CPU在长期被工作线程使用的情况下,尽可能的少的分配线程,即适用执行长期的任务。

6.2 CachedThreadPool

可缓存线程池,线程数根据任务动态调整的线程池

  • 线程池参数
    • 核心线程数为0
    • 最大线程数为Integer.MAX_VALUE
    • 工作队列是SynchronousQueue同步队列
    • 非核心线程空闲存活时间为60秒
  • 工作机制
    • 提交线程任务
      • 因为核心线程数为0,所以任务直接加到SynchronousQueue工作队列
        -判断是否有空闲线程,如果有,就去取出任务执行
      • 如果没有空闲线程,就新建一个线程
      • 执行执行完任务的线程,还可以存活60秒,如果在这期间,接到任务,可以继续存活下去;否则,被销毁。
  • 使用场景: 用于并发执行大量短期的小任务。

6.3 SingleThreadExecutor

单线程化的线程池

  • 线程池参数
    • 核心线程数为1
    • 最大线程数也为1
    • 阻塞队列是LinkedBlockingQueue
    • 非核心线程空闲存活时间为0秒
  • 使用场景: 适用于串行执行任务的场景,将任务按顺序执行。

6.4 ScheduledThreadPool

能实现定时、周期性任务的线程池

  • 线程池参数
    • 最大线程数为Integer.MAX_VALUE
    • 阻塞队列是DelayedWorkQueue
    • keepAliveTime为0
  • 使用场景:周期性执行任务,并且需要限制线程数量的需求场景。

总结

以上就是线程池的介绍,并不推荐使用Executors工具类创建的线程池,因为这样创建出来的线程池无法给线程命名,而且不灵活,无法根据需求设定需要的参数(核心线程数,最大线程数,非核心线程存活时间,阻塞队列),所以建议使用ThreadPoolEexcutor自己创建线程,灵活使用;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值