ThreadPoolExecutor功能特性

简介: ThreadPoolExecutor功能特性
  1. 核心和最大线程大小

ThreadPoolExecutor会根据CorePoolSize和maximumPoolSize来自动调整线程池大小。当一个新任务到来且线程池的中线程数量小于corePoolSize且其他工作线程空闲时,ThreadPoolExecutor会创建一个新线程处理这个请求。
ThreadPoolExecutor只会在任务队列已经满了并且线程数量大于corePoolSize但是小于maximumPoolSize时才会新建线程处理新的请求。
通过将corePoolSize和maximumPoolSize可以得到一个固定线程大小的线程池。
通过将maximumPoolSize设置为一个很大的数字,比如:Integer.MAX_VALUE,并且把这种设置放到生产环境上,那么你可能晚上睡不着觉。
一般情况下,线程池的core和maximum大小是通过ThreadPoolExecutor的构造函数来设置的,但是也可以通过setCorePoolSize(int) 和setMaximumPoolSize来更改。

  1. 按需新建线程

新建ThreadPoolExecutor后,它并不会直接按照corePoolSize的数量新建线程,而是当提交任务后才会新建线程。
但是我们可以通过调用prestartCoreThread()或者prestartAllCoreThreads来启动线程。
只有你创建了一个有任务的队列时,你才可能想要预启动线程。

  1. 创建线程方式

线程池中创建线程会使用ThreadFactory。如果你在创建线程池时没有指定ThreadFactory,那么ThreadPoolExecturo会使用Executors.defaultThreadFactory()来创建普通优先级且非守护线程。
通过使用不通的ThreadFactory,你可以修改线程的名称,线程组,优先级和是否为守护进程。
如果ThreadFactory在创建线程时返回了一个null对象,那么ThreadPoolExecutor会继续执行,但是并不会执行任务。

  1. 保活时间

如果线程池当前的线程数量大于corePoolSize,并且这些大于corePoolSize的线程的空闲时间已经超过了keepAliveTime设置的时间,那么这些线程会被终止。
在线程池没有被使用时,这种方式可以减少对资源的消耗。
如果过一段时间线程池又被激活了,那么线程池会再次创建新的线程。
keeAliveTime可以通过调用setKeepAliveTime来动态修改。如果keepAliveTime使用Long.MAX_VALUE就意味着你不想终止空闲的线程。
默认情况下,保活策略只有当线程池的线程数量大于corePoolSize时才会生效,但是你可以通过调用allowCoreThreadTimeOut函数针对核心线程执行超时策略。

  1. 任务队列

只要是实现了BlockingQueue接口的队列都可以作为任务队列参数传递给线程池。线程池对于队列大小的使用:

  • 如果当前正在运行的线程大小小于corePoolSize,那么线程池会一直添加新的线程
  • 如果当前线程池线程大小达到或超过了corePoolSize的话,那么线程池会先将任务入队,而不是创建一个新的线程
  • 如果一个新任务无法入队,如果线程池大小没有超过了maximumPoolSize的话,那么线程池会创建一个新线程,否则新任务会直接执行拒绝策略

队列的常用策略:

  • 直接传递

提交新任务后,新任务需要立即执行。在这种场景下,你需要设置一个无上限的MaximumPoolSize。

  • 无界队列

使用一个无界的队列(例如:LinkedBlockingQueue)在核心线程都在忙的时候任务一直等待。因为使用无界队列时,新任务可以一直入队,那么线程池中处于运行状态的线程就不会超过corePoolSize设置的大小,这也就意味着在创建线程池时指定的的MaximumPoolSize不会起作用。无界队列可以用于web Server。

  • 有界队列

有界队列可以有效的防止资源耗尽,但是会有点难以调整和控制。需要权衡队列大小和最大线程大小:如果使用大队列和小的线程大小,那么会有减少使用CPU利用率、操作系统资源和上下文切换的开销,但是会导致吞吐量降低;如果任务频繁阻塞(比如:任务是IO密集型的),那么系统的调度时间可能多于任务的执行时间。;如果使用小队列和大的线程池,那么会有很多CPU的调度开销,这当然也会降低系统的吞吐量

  1. 任务拒绝策略

拒绝策略发生条件:

  • 线程池已经终止
  • 线程池运行线程已经达到maximum指定的大小且队列已满

预定义的的拒绝策略:

  • 默认拒绝策略:AbortPolicy

该拒绝策略是直接抛出一个运行时异常RejectedExecutionException。

  • CallerRunsPolicy

该拒绝策略是提交任务的线程执行该任务。

  • DiscardPolicy

该拒绝策略是将任务丢弃。

  • DiscardOldPolicy

该拒绝策略是将任务队列中第一个任务丢弃,并尝试将该任务重新入队。
自定义策略

  • 自定义拒绝策略只需要实现RejectedExecutionHandler接口实现void rejectedExecution(Runnable r,

ThreadPoolExecutor executor);这个函数即可。

  1. 回调函数

ThreadPoolExecutor提供了beforeExecute(Thread, Runnable)和afterExecute(Runnable, Throwable)方法,表示在每个任务执行前和每个任务执行后执行该函数。例如:可以重新初始化ThreadLocal线程局部变量,统计数据或添加日志。另外,terminated()函数可以被重新来执行在线程池完全终止后的一些特殊业务处理。
如果钩子后者回调方法抛出了异常,那么可能内部的工作线程可能已经失败了或已经终止了。

  1. 队列维护

线程池提供了getQueue()允许用户来监控和调试。基于其他目的调用getQueue()并不鼓励。ThreadPoolExectutor提供了两个方法:remove(Runnable)和purge()可以用于在大量任务被取消时协助回收存储。

  1. 线程池终止

如果线程池没有任务执行并且已经没有了驻留的线程时,那么线程池就自动停止了。基于这个特性,那么你想要保证最终未使用的线程池回收或者用户忘记了调用shutdown()函数,那么你可以通过设置合适的保活时间和0个核心线程或通过设置allowCoreThreadTimeOut(boolean),你可以将未使用的线程死亡。

目录
相关文章
|
前端开发 Dubbo Java
医疗管理系统-项目概述和环境搭建
医疗管理系统-项目概述和环境搭建
405 0
|
JavaScript 前端开发 程序员
用Unity不会几个插件怎么能行?Unity各类插件及教程推荐
话说工欲善其事必先利其器,程序员总是有一些开发利器,而对于Unity3D开发程序员来说,插件就是非常好用的利器。 今天博主,就将比较好用的插件推荐给大家,希望一起学习品鉴。
|
Ubuntu 网络安全 C语言
【VirtualBox安装Ubuntu过程记录】
【VirtualBox安装Ubuntu过程记录】
416 2
|
12月前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的在线摄影预约管理系统
基于Java+Springboot+Vue开发的在线摄影预约管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的在线摄影管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
222 8
基于Java+Springboot+Vue开发的在线摄影预约管理系统
|
SQL 关系型数据库 MySQL
一文带你了解MySQL的DCL语句
一文带你了解MySQL的DCL语句
722 1
|
Java
Java Exception打印及输出到日志
有时候如果打印出异常的错误,并记录下来,这里记录一下
799 5
|
关系型数据库 MySQL
MySQL中TIMESTAMPDIFF和TIMESTAMPADD函数的用法
MySQL中TIMESTAMPDIFF和TIMESTAMPADD函数的用法
588 0
|
人工智能 编解码 Java
用户投稿:这款 AI 编码工具 CodeFuse 很惊艳
在 AI 时代,总是会迅速出现很多惊艳的产品工具,这些效率工具,在很大程度上推动了科技的进步。特别是在编程领域,各类工具更是层出不穷,从 GitHub Copilot 到 CodeGeeX,再到通义灵码,有很多工具在不断涌现。今天,我想和大家分享一款我最近发现的、非常出色的编程辅助工具 —— CodeFuse。
565 0
用户投稿:这款 AI 编码工具 CodeFuse 很惊艳
|
Kubernetes 容器
搭建K8S环境单机K8S集群
搭建K8S环境单机K8S集群
625 0
|
安全 关系型数据库 Shell
PostgresSQL未授权导致命令执行
PostgresSQL未授权导致命令执行
563 0