线程池

线程是一种昂贵的资源,主要开销包括:

线程的创建与启动,线程启动会产生相应的线程调度开销,线程调度会导致线程上下文切换,从而增加处理器资源的消耗。

因此我们需要一种有效使用线程的方式

线程池是一种基于池化思想的线程管理工具,它在池内维护多个长期存在的"常驻线程",通过重复使用这些"常驻线程"来避免线程的反复创建和销毁

线程池核心参数:

1.corePoolSizeint: 线程池核心线程的数量。核心线程就是如果不做特殊设置就永远不会停止的线程,即使没有任务执行了,核心线程也不会被回收掉。线程池总是会保证最少corePoolSize个线程存在。

2.maximumPoolSizeint:线程池的最大线程数量,它表示线程池的最大线程数限制

3.keepAliveTimelong:定义的空闲时间,当任务被消耗完毕后,高于corePoolSize的这部分线程在空闲多长时间会被关闭回收掉

4.unitTimeUnit:空闲的时间单位,可以指定为秒、分钟、小时等单位信息,与keepAliveTime共同使用,共同定义空闲时间。

5.workQueueBlockingQueue:任务队列,它是一个队列结构的容器。正常情况下,任务被提交到线程池之后,会立即被核心线程所执行,但是当核心线程都处于忙碌状态的时候,没有核心线程去执行这个任务,那么这个任务会被暂时提交到任务队列中等待核心线程空闲下来再去执行

6.threadFactoryThreadFactory:线程工厂,主要用于控制线程池以何种方式去产生一个新线程

7.handlerRejectedExecutionHandler:这个参数就是线程池在处理突发大量任务的最后的 兜底手段。当 corePoolSize、maximumPoolSize、workQueue 全部都被任务填满了之后,线程池会认为已经无力再执行后续提交的任务,此时对于后续的任务会触发拒绝策略来拒绝任务

线程池中任务提交流程:

workQueue 的常用类型

所谓的工作队列事实上就是一个"等待任务"的临时存放的容器,这个容器 JDK 官方规定必须是一个阻塞的队列

JDK 中为我们提供了很多的阻塞队列,在线程池中常用的队列有以下四种。

  • java.util.concurrent.SynchronousQueue:该队列没有容量,只是做一个简单的交换。因为没有容量,线程池内的线程数可以很轻松地达到 maximumPoolSize 设置的容量。
  • java.util.concurrent.LinkedBlockingQueue :无界队列,所谓无界队列的意思就是它没有边界,大小近乎无限,队列容量为Integer.MAX_VALUE。使用这种队列的时候需要特别注意,因为它的容量近乎无限,所以线程池参数 maximumPoolSize 是不生效的,拒绝策略也是失效的,因为队列永远也装不满;所以在任务的执行速度低于任务产生的情况下,众多的任务可能被无限地堆积在无界队列中,最终导致 OOM 的发生。
  • java.util.concurrent.ArrayBlockingQueue :有界队列,它的概念与无界队列恰恰相反,它可以设置一个长度,这种情况下maximumPoolSize和拒绝策略就有了意义,当队列被塞满后就会执行我们分析的逻辑。
  • java.util.concurrent.ScheduledThreadPoolExecutor.DelayedWorkQueue :延时队列,它可以写入一个任务并定义一个时间,这个任务只有在达到超时时间后才能被消费,这种队列适用于定时线程池

拒绝策略类型

JDK 官方为我们默认提供了 4 种拒绝策略。

  • java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy:当满足拒绝策略时,丢弃任务队列中旧的任务,将新任务添加到任务队列。
  • java.util.concurrent.ThreadPoolExecutor.AbortPolicy:当满足拒绝策略时,提交的任务会直接抛出 RejectedExecutionException 异常。
  • java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy:当满足拒绝策略时,被拒绝的任务会交由提交任务的那个线程去执行,谁提交的谁执行。
  • java.util.concurrent.ThreadPoolExecutor.DiscardPolicy:当满足拒绝策略时,新提交的任务会被静默丢弃,不会出现任何异常。

当我们的系统对于某一个任务特别敏感的时候,就是即使线程池处理不了了,那么这个任务也必须执行,此时就可以使用 CallerRunsPolicy,它会直接让主线程来执行。比如,A 线程向线程池提交任务,结果线程池处理不了了,那么这个拒绝策略就会直接让 A 线程自己去执行这个任务!从而保证任务一定能够被执行。但是注意,这种拒绝策略会导致调用者线程阻塞。

也可以自定义拒绝策略,比如我们在线程池满了之后,输出一行丢弃的日志之后将任务丢弃,只需要实现 java.util.concurrent.RejectedExecutionHandler 接口即可,具体的定义方式如下:

threadFactory

JDK 默认使用DefaultThreadFactory作为线程池的线程池工厂,默认线程池创建的线程都属于同一个线程组,拥有同样的优先级,并且都不是守护线程。线程工厂主要规定了线程池如何创建线程

JDK 默认的线程池创建方式

(1)定长线程池

定长线程池的意义是事先就规定好了线程池的大小,它的corePoolSizemaximumPoolSize数量是相等的,且线程队列使用的是无界队列 。那么根据我们上文的分析,当corePoolSize=maximumPoolSize,而且队列为无界队列的时候,永远也不会触发拒绝策略,而且所有来不及执行的任务都会堆积在任务队列中。

缺陷:因为任务只会无限制地堆积在任务队列中,当任务产生速度过快的时候,线程池无法自行扩展,而且也无法执行拒绝策略,那么任务将会全部堆积在无界队列中,进而产生 OOM 问题。

(2)简单线程池

它的配置和定长线程池几乎一致,唯一不同的是它的corePoolSizemaximumPoolSize都是 1,证明它最多只能同时执行 1 个任务,多余的任务会被缓存在无界队列中等待消费,缺陷与定长线程池一致。

(3)缓存线程池

缓冲线程池是一个特殊的线程池,它的特性是来多少任务,我开启多少线程,当任务执行完毕后,线程空闲一定时间后会被回收。

缺陷:极限情况下会导致线程无限制地创建线程,最终将系统资源全部消耗。

(4)定时线程池

这个线程池就很特殊了,它属于 ThreadPoolExecutor 的衍生子类,作用是可以以一个固定的时间去不断地执行任务。

这个线程池也是一个特殊的线程池,与上面线程池不同的是,这个线程池是带有定时功能的线程池,它可以将一个任务延时一定的时间后执行,也可以让任务以一个固定的频率去执行。

自定义线程池

使用系统自带的线程池定义方式,那么开发人员对于线程池的参数是无法掌控的,我们所使用的参数全部都变成系统预设的,所以在开发中,我们使用最多的还是自定义线程池。

java 复制代码
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 2, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10), new ThreadFactory() {
    @Override
    public Thread newThread(Runnable r) {
        return new Thread(r);
    }
}, new ThreadPoolExecutor.AbortPolicy());

线程池的主要 API

提交任务:

线程池中提交任务的方式主要有两种:

1.一种是没有任何返回值和异常的提交方式:execute 方法

2.一种是提交任务后,可以获取任务的返回值、执行异常的提交方式:submit 方法

停止线程池:

shutdown :线程池会停止接收新任务。此时如果再调用 shutdown 后再去提交任务,线程池会将任务直接推送到拒绝策略去执行

shutdownNow :立即停止线程池,包括堆积在队列里面的任务

相关推荐
姜学迁1 小时前
Rust-枚举
开发语言·后端·rust
爱学习的小健2 小时前
MQTT--Java整合EMQX
后端
北极小狐2 小时前
Java vs JavaScript:类型系统的艺术 - 从 Object 到 any,从静态到动态
后端
【D'accumulation】2 小时前
令牌主动失效机制范例(利用redis)注释分析
java·spring boot·redis·后端
2401_854391082 小时前
高效开发:SpringBoot网上租赁系统实现细节
java·spring boot·后端
Cikiss2 小时前
微服务实战——SpringCache 整合 Redis
java·redis·后端·微服务
Cikiss2 小时前
微服务实战——平台属性
java·数据库·后端·微服务
OEC小胖胖3 小时前
Spring Boot + MyBatis 项目中常用注解详解(万字长篇解读)
java·spring boot·后端·spring·mybatis·web
2401_857617623 小时前
SpringBoot校园资料平台:开发与部署指南
java·spring boot·后端
计算机学姐3 小时前
基于SpringBoot+Vue的在线投票系统
java·vue.js·spring boot·后端·学习·intellij-idea·mybatis