常用并发工具类(线程池)

文章目录

  • 概述
  • ThreadPoolExecutor
    • ThreadPoolExecutor 的主要属性
    • Worker 主要属性
  • 线程池的状态
    • 线程池的状态流转
  • 线程池提交任务的执行流程
  • 线程数量的设置
  • 线程池的种类
    • FixedThreadPool
    • CachedThreadPool
    • SingleThreadExecutor
    • ScheduledThreadPoolExecutor
    • SingleThreadScheduledExecutor
    • ForkJoinPool
      • work-stealing
      • ForkJoinPool 注意事项

概述

线程池,是资源池化思想的一种实现。线程是一种宝贵且有限的 CPU 资源,一个线程的创建跟销毁的成本是比较高的。
所以创建线程池,主要有以下两个目的:

  • 复用线程:单个线程创建使用完毕后,可以不用立马销毁,而是把这个线程放入到线程池中,等待下次执行任务
  • 管理线程:线程是一种宝贵且有限的 CPU 资源,其数量并不是无上限的
    • 可以通过线程池来限制创建线程的数量,也可以通过线程池来决定何时销毁冗余创建的线程
    • 在线程池中存在多个线程时,可以通过线程池来协调每个线程的任务执行情况,从而避免出现线程处于空闲状态,造成线程资源的浪费

ThreadPoolExecutor

ThreadPoolExecutor 是一个通用的线程池的实现,类图如下所示:
在这里插入图片描述

可以看到,ThreadPoolExecutor 主要实现了 ExecutorExecutorServiceAbstractExecutorService

ThreadPoolExecutor 的主要属性

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));private final BlockingQueue<Runnable> workQueue;private final ReentrantLock mainLock = new ReentrantLock();/*** Set containing all worker threads in pool. Accessed only when* holding mainLock.*/private final HashSet<Worker> workers = new HashSet<Worker>();/*** Wait condition to support awaitTermination*/private final Condition termination = mainLock.newCondition();/*** Tracks largest attained pool size. Accessed only under* mainLock.*/private int largestPoolSize;/*** Counter for completed tasks. Updated only on termination of* worker threads. Accessed only under mainLock.*/private long completedTaskCount;private volatile ThreadFactory threadFactory;/*** Handler called when saturated or shutdown in execute.*/private volatile RejectedExecutionHandler handler;/*** Timeout in nanoseconds for idle threads waiting for work.* Threads use this timeout when there are more than corePoolSize* present or if allowCoreThreadTimeOut. Otherwise they wait* forever for new work.*/private volatile long keepAliveTime;/*** If false (default), core threads stay alive even when idle.* If true, core threads use keepAliveTime to time out waiting* for work.*/private volatile boolean allowCoreThreadTimeOut;/*** Core pool size is the minimum number of workers to keep alive* (and not allow to time out etc) unless allowCoreThreadTimeOut* is set, in which case the minimum is zero.*/private volatile int corePoolSize;/*** Maximum pool size. Note that the actual maximum is internally* bounded by CAPACITY.*/private volatile int maximumPoolSize;/*** The default rejected execution handler*/private static final RejectedExecutionHandler defaultHandler =new AbortPolicy();
  • 内置一个 AtomicInteger,高 3 位用于表示线程池当前状态,低 29 位用于表示线程个数
  • 内置一个阻塞队列,用于核心线程都处于执行状态后保存任务
  • 内置一个 ReentrantLock 独占锁,以及一个由锁对象创建出来的条件对象 Condition termination
  • 一个去重集合 HashSet<Worker> workers,用于保存线程
  • 历史出现过的最多线程数量 int largestPoolSize
  • 已完成的任务数量 long completedTaskCount
  • volatile 关键字修饰的线程组 ThreadFactory threadFactory
  • volatile 关键字修饰的拒绝策略 RejectedExecutionHandler handler
  • volatile 关键字修饰的空闲线程最大存活时间 long keepAliveTime
  • volatile 关键字修饰的是否允许核心线程超时 boolean allowCoreThreadTimeOut
  • volatile 关键字修饰的核心线程数量 int corePoolSize
  • volatile 关键字修饰的最大线程数量 int maximumPoolSize
  • 默认的拒绝策略 new AbortPolicy(),即默认的拒绝策略是抛出异常

Worker 主要属性

Worker 即工作线程。Worker 继承了 AQS 并实现了 Runnable 接口,主要有以下几个属性:

  • Thread thread:线程对象
  • Runnable firstTask:首次执行的任务

线程池的状态

线程池一共有 5 个状态,分别是:

  • RUNNING:运行状态,接受新任务并且处理阻塞队列里的任务
  • SHUTDOWN:标记关闭状态,拒绝新任务,但是处理正在执行和阻塞队列里的任务
  • STOP:关闭状态,拒绝新任务并且清空阻塞队列,同时会中断所有线程
  • TIDYING:清场状态,线程池和阻塞队列都为空,将要调用 terminated() 方法
  • TERMINATED:终止状态。这是当 terminated() 方法调用完成之后的状态

线程池的状态流转

  • 线程池一旦被创建,就是 RUNNING 状态,可以正常地执行任务,以及接受新任务
  • RUNNING 状态调用 shutdown() 方法后,线程池就会切换到 SHUTDOWN 状态,标记关闭状态下
    • 遍历线程池,当前所有空闲线程都会被调用 interrupt() 方法设置中断( 被设置中断的线程将会从线程池中移除)
    • 不再接受新任务,新提交的任务将直接丢弃
    • 当前正在执行任务的线程把正在执行的任务继续完成,且这些线程将会继续从阻塞队列中获取任务执行
  • RUNNING 或者 SHUTDOWN 状态调用 shutdownNow() 方法后,线程池就会切换到 STOP 状态
    • 不再接受新任务
    • 遍历线程池,调用每个线程的 interrupt() 方法设置中断
    • 遍历阻塞队列,移除所有任务
  • SHUTDOWN 或者 STOP 状态持续一段时间后,当线程池中没有线程,并且阻塞队列也为空后,线程池将会切换到 TIDYING 状态
  • TIDYING 状态将会做最后的收尾工作,确保所有资源都被释放

线程池提交任务的执行流程

  • 当有新任务提交时,判断当前核心线程数是否小于最大核心线程数
    • 如果小于最大核心线程数,则创建一个新线程执行任务
  • 如果大于核心线程数,则尝试将任务提交到阻塞队列中
    • 如果阻塞队列未满,则将任务提交到阻塞队列中
  • 如果阻塞队列已满,则尝试开启新线程
    • 如果当前线程数量小于最大线程数,则创建一个新线程执行任务
    • 如果当前线程数量等于最大线程数,则执行拒绝策略
      • AbortPolicy:直接抛出异常
      • CallerRunsPolicy:由提交任务的线程执行
      • DiscardPolicy:直接丢弃任务
      • DiscardOldestPolicy:将阻塞队列队头的任务取出并丢弃,然后重试提交

线程数量的设置

线程数量的经验计算公式为:

线程数 = CPU 核心数 *(1 + 平均等待时间 / 平均工作时间)

线程池的种类

FixedThreadPool

FixedThreadPool,即固定数量线程池,特点是核心线程数量 = 最大线程数量,且阻塞队列容量较大甚至无界

  • 固定数量,意味着线程不会频繁地创建和销毁,在线程数量达到核心线程数量后,在执行任务过程中没有发生异常的情况下,线程数量将不会再发生变化
  • 阻塞队列容量非常大,代表着任务可以被无条件地,一直不断地添加进来,可能会引发 GC 问题
    • 当任务的执行速度远小于任务的添加速度时,可能会导致阻塞队列中的节点数量特别巨大,吃光堆内存空间,导致 OOM

FixedThreadExecutor 适用于计算密集型任务, 确保 CPU 在长时间被单个工作线程使用的情况下, 尽可能少地创建、分配、销毁线程, 即适用于长期且数量可控的任务

CachedThreadPool

CachedThreadPool,即缓存线程池,特点是

  • 核心线程数量为 0,意味着在线程池空闲时,不会占用任何的线程资源
  • 最大线程数非常巨大,意味着可以创建非常多的线程,可能会导致系统线程资源耗尽的问题
  • 线程有较短的最大存活时间(如 60s),意味着每个工作线程的存活时间比较短
  • 阻塞队列不存储元素,意味着每次有新任务提交时,要么可以立马被一个空闲线程执行,要么可以立马创建一个新线程执行去执行

CachedThreadPool 适用于存在数量多且耗时少的任务场景,由于未限制线程最大数量,在任务的执行速度远小于任务的添加速度时,有可能会导致系统线程资源耗尽或引发 OOM

SingleThreadExecutor

SingleThreadExecutor,即单线程线程池,特点是:

  • 核心线程数量 = 最大线程数量 = 1,意味着线程池中最多只会有一个线程,意味着所有提交的任务都会被串行化执行,任意时刻不会有同时两个任务在被同时执行
  • 阻塞队列容量非常大甚至无界,代表着任务可以被无条件地,一直不断地添加进来,可能会引发 GC 问题

SingleThreadExecutor 适用于多个任务需要串行化执行的场景,由于阻塞队列无界,还是有可能引发 OOM
注意,Executors.newSingleThreadExecutor() 并不等价于 Executors.newFixedThreadPool(1)Executors 中的源码为:

public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}

可以看到,newSingleThreadExecutor() 方法创建出来的 ThreadPoolExecutor 对象被包了一层 FinalizableDelegatedExecutorService,所以这两者不等价

ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor,是定时任务线程池,可以使用它来实现定时任务,主要的构造方法为:

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

可以看到,ScheduledThreadPoolExecutor 的特点是:

  • 最大线程数无界
  • 空闲线程的最大存活时间为 0,即在线程池不会存储空闲线程
  • 阻塞队列 DelayedWorkQueue 是一个底层数据结构为堆的延迟阻塞队列,无界

SingleThreadScheduledExecutor

SingleThreadScheduledExecutor 即单线程定时任务线程池,可以保证所有定时任务都会由一个线程串行执行

ForkJoinPool

ForkJoinPool,是一个用于管理 ForkJoinWrokerThread 的线程池,ForkJoin 是一种基于分治思想的并发编程框架,通过将任务分解后使用多个线程并行计算,最后合并所有子任务的计算结果得到最终的计算结果。
ForkJoinPool 线程池可以把一个大任务拆分成小任务并行执行,但是任务类必须继承自 RecursiveTask 或者 RecursiveAction

work-stealing

工作窃取算法(work-stealing)算法是指某个线程从其他队列里窃取任务来执行。主要实现的思路有一下几个关键点:

  • 每个工作者线程都会对应一个双端任务队列
  • 当某个工作者线程自己的任务队列中的任务执行完毕之后,将会从其他工作者线程的任务队列里窃取任务出来执行
  • 工作者线程从自己的任务队列的头部来获取任务(removeFirst
  • 工作者线程执行任务窃取时,将从其他工作者线程的任务队列的尾部来获取任务(removeLast

ForkJoinPool 注意事项

  • 任务类必须继承自 RecursiveTask 或者 RecursiveAction
  • 子任务间没有相互依赖
  • 任务最好不要包含阻塞 IO 操作

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/557276.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM 内存模型与内存分配方式

文章目录JVM 内存模型概述基于分代收集理论设计的垃圾收集器所管理的堆结构方法区的演变内存分配划分内存的方法划分内存时如何解决并发问题对象栈上分配基于分代收集理论的垃圾收集器管理下的内存分配规则对象优先分配在 Eden 区大对象直接进入老年代长期存活的对象将逐步进入…

image pil 图像保存_如何利用python中的PIL库做图像处理?

自从这个世界上出现了Python编程&#xff0c;一切都好像有了新的思路与进展&#xff0c;比如人工智能&#xff0c;还有我们常用的PS&#xff0c;你可知道Python也可以做图像处理&#xff0c;用的就是PIL库&#xff0c;还没有用过的&#xff0c;还没有发现的&#xff0c;还没有实…

link st 量产工具_ST-Link资料03_ST-Link固件升级、驱动下载安装方法

说明&#xff1a;本文原创作者『strongerHuang』本文首发于微信公众号『嵌入式专栏』&#xff0c;同时也更新在我的个人网站&#xff1a;EmbeddedDevelop一、写在前面前两篇文章讲述的都是关于ST-Link的一些理论知识&#xff0c;建议初学者看看&#xff1a;ST-Link资料01_ST-Li…

SSM 框架整合 spring 发送邮件功能实现!

基于SSM的 spring 发送邮件的实现 由于考虑到项目中需要&#xff0c;如果程序出现异常或其它问题&#xff0c;可以发送邮件来及时提醒后台维护人员处>理。所以目前考虑使用JavaMail来实现邮件发送&#xff01;如下&#xff0c;是我整理的一些内容&#xff0c;做个笔记记录下…

Java 故障处理与性能监控工具

文章目录概述基础工具jpsjstatjinfojmapjhatjstack高级工具VisualVMVisualVM 的主要功能ArthasGC Easy概述 在使用 Java 语言进行开发的过程中&#xff0c;我们很可能会遇到各种程序问题。 比如&#xff0c;可能会遇见程序突然就静止不动了&#xff0c;但是程序进程依然显示在…

SSM整合框架实现发送邮件功能

SSM整合框架实现发送邮件功能 1.导入发送邮件的依赖 <!-- 发送邮件jar包--><!--spring支持--><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>5.0.0.RELE…

ap接口 php_2018年小米高级 PHP 工程师面试题(模拟考试卷)

点击蓝字关注我们&#xff01;每天获取最新的编程小知识&#xff01;源 / php中文网 源 / www.php.cn在面试之前多看看有关公司的面试资料&#xff0c;对之后的面试会很有帮助。今天就给大家带来2018年小米高级 PHP 工程师面试题(模拟考试卷)&#xff0c;有着一定的参考价…

composer 设置版本号_Composer依赖管理 – PHP的利器

别再到处搜PHP类扩展包了&#xff0c;对于现代语言而言&#xff0c;包管理器基本上是标配。Java 有 Maven&#xff0c;Python 有 pip&#xff0c;Ruby 有 gem&#xff0c;Nodejs 有 npm。PHP 的则是 PEAR&#xff0c;不过 PEAR 坑不少&#xff1a;依赖处理容易出问题配置非常复…

SpringBoot2.5.4发送邮件4种方式

一.准备 在创建SpringBoot项目 二、选择依赖 选择依赖时 发现其选择依赖时有邮件发送与Springboot整合的jar包&#xff0c;我们勾选即可 如果未勾选也不要紧&#xff0c;咱们手动导入 <dependency><groupId>org.projectlombok</groupId><artifactId…

手游传奇刷元宝_传奇手游 平民制霸刀刀爆元宝!

新轩辕神途手游游戏介绍新轩辕神途是一款玩法种类十分丰富多样的神途手游&#xff0c;游戏内拥有放置挂机升级玩法&#xff0c;玩家不用浪费时间工作上学都能自动升级打宝&#xff0c;更有十分庞大的世界地图等你来探索&#xff0c;十分靠谱&#xff0c;更受欢迎&#xff0c;爆…

MySQL 逻辑架构与常用的存储引擎

文章目录概述逻辑架构示意图Server 层功能模块连接器查询缓存分析器优化器执行器存储引擎层InnoDBInnoDB 主要特性InnoDB 引擎下的查询过程MyISAMMyISAM 的主要特性MyISAM 引擎下的查询过程InnoDB 和 MyISAM 的对比概述 MySQL 是我们平时开发中最常用的关系型数据库&#xff0…

java 阿里云接口实现发送短信验证码

1.先去阿里云开通短信服务&#xff1a; 2.添加模板及签名&#xff1a;需要审核&#xff0c;个人账户审核就几分钟就OK 先解释一下模板及签名&#xff1a; 标准参照&#xff1a;https://help.aliyun.com/document_detail/55324.html?spm5176.sms-sign.0.0.765c1cbeNhvWBZ 去…

MySQL 索引底层数据结构实现

文章目录概述讨论范围查询数据结构查询数据结构种类及其高性能查询原理MySQL 索引的底层数据结构MySQL 索引的需求分析选择 MySQL 索引的底层数据结构B- 树和 B 树的对比MySQL 索引的底层数据结构揭秘概述 MySQL 的索引是存储引擎用于快速找到记录的一种数据结构&#xff0c;是…

Java面试——SpringMVC系列总结

文章目录&#xff1a; 1.什么是Spring MVC&#xff1f; 2.Spring MVC的主要组件有哪些&#xff1f; 3.请描述一下Spring MVC的工作流程&#xff1f; 4.MVC是什么&#xff1f;MVC设计模式的好处有哪些 5.拦截器Interceptor与过滤器Filter有什么区别&#xff1f; 6.Spring …

中蒙俄经济走廊背景_上海外国语大学师生代表团参观访问G60科创走廊俄罗斯院士创新基地...

10月23日&#xff0c;上海外国语大学团委书记、创新创业与实践教育学院执行院长廖文其、俄罗斯东欧中亚学院党总支副书记郝佳、辅导员石朝天及学生代表等一行15人参观访问G60科创走廊俄罗斯院士创新基地(下简称&#xff1a;创新基地)。创新基地主任赵磊、俄罗斯中小企业联合会华…

MySQL 索引类别与索引使用指南

文章目录概述MySQL 索引类型MySQL 索引方法BTREE 方法HASH 方法主键构成的索引结构主键索引的优点主键索引的缺点依赖顺序插入更新代价高索引使用指南索引树回顾索引树排序规则最左前缀法则最左前缀法则的产生依据最左前缀法则延申字段书写顺序不影响最左前缀法则最左前缀法则总…

测试员不可不知的几款bug管理工具

根据每个公司性质的不同&#xff0c;规模的不同&#xff0c;所用到的bug管理工具也可能不同。你们用的bug管理工具是什么呢&#xff1f;下面介绍几款主流的bug管理工具&#xff1a; 1. JIRA&#xff08;付费&#xff09; JIRA的生产者把JIRA定义为Professional Issue Tracker&…

Bugzilla 使用指南

本篇文章主要关注于如何高效合理的使用Bugzilla。 Bugzilla是一个开源的缺陷跟踪系统&#xff0c;它可以管理软件开发过程中缺陷的提交、修复、关闭等整个生命周期。 1. 基本概念 在Bugzilla中&#xff0c;Bug报告状态分为以下几种状态&#xff0c; 待确认的 unconfirmed 新…

MySQL explain 命令

概述 MySQL 的 explain 命令&#xff0c;主要用于查看实际查询过程中的一些执行细节&#xff08;执行计划&#xff09;&#xff0c;也是查看优化器决定如何执行查询的主要方法 explain 使用示例 explain 的使用也很简单&#xff0c;在 select 语句之前增加 explain 关键字再…

centos6.5安装bugzilla超详细教程

经过自己的摸索&#xff0c;与尝试&#xff0c;成功在centos6.5上&#xff0c;安装bugzilla。并且可以发送邮件。 一、安装软件 首先&#xff0c;需要安装一些软件 yum install mysql-devel -y yum install mysql-server -y yum install httpd -y yum install gcc gcc-…