JUC之线程池的标准创建方式

news2024/9/21 1:35:44

文章目录

      • JUC之线程池的标准创建方式
        • 核心和最大线程数量
        • 空闲时长(keepAliveTime)
        • 线程工厂(ThreadFactory)
        • 任务阻塞队列
        • 线程池的拒绝策略
        • 线程池的任务调度流程

JUC之线程池的标准创建方式

​ 因为使用Executors快捷创建线程池在使用时会有严重的潜在问题,因此在实战使用时一般不使用快捷创建线程池的方式在创建线程池,而是使用ThreadPoolExecutor的标准构造器去构建线程池。Executors工厂类中创建线程池的快捷工厂方法实际上是调用 ThreadPoolExecutor(定时任务使用ScheduledThreadPoolExecutor) 线程池的构造方法完成的。

​ ThreadPoolExecutor构造方法有多个重载版本,较重要的一个构造器包含七个参数,如下:

public ThreadPoolExecutor(int corePoolSize,//核心线程数,即使线程空闲也不会回收
                              int maximumPoolSize,//最大线程数
                              long keepAliveTime,//线程的最大空闲时长
                              TimeUnit unit,//时长单位
                              BlockingQueue<Runnable> workQueue,//任务队列
                              ThreadFactory threadFactory,//线程工厂,新线程的创建方式
                              RejectedExecutionHandler handler) //拒绝策略

核心和最大线程数量

​ 参数corePoolSize用于设置核心(Core)线程池数量,参数 maximumPoolSize用于设置最大线程数量。线程池执行器将会根据 corePoolSize和maximumPoolSize自动维护线程池中的工作线程,大致 规则为:

  1. 当在线程池接收到新任务,并且当前工作线程数少于 corePoolSize时,即使其他工作线程处于空闲状态,也会创建一个新 线程来处理该请求,直到线程数达到corePoolSize。
  2. 如果当前工作线程数多于corePoolSize数量,但小于 maximumPoolSize数量,那么仅当任务排队队列已满时才会创建新线 程。通过设置corePoolSize和maximumPoolSize相同,可以创建一个固 定大小的线程池。
  3. 当maximumPoolSize被设置为无界值(如 Integer.MAX_VALUE)时,线程池可以接收任意数量的并发任务。
  4. corePoolSize和maximumPoolSize不仅能在线程池构造时设 置,也可以调用setCorePoolSize()和setMaximumPoolSize()两个方法 进行动态更改。

空闲时长(keepAliveTime)

​ 线程构造器的keepAliveTime(空闲线程存活时间)参数用于设置 池内线程最大Idle(空闲)时长(或者说保活时长),如果超过这个 时间,默认情况下Idle、非Core线程会被回收。

​ 如果池在使用过程中提交任务的频率变高,也可以调用方法 setKeepAliveTime(long,TimeUnit)进行线程存活时间的动态调整, 可以将时长延长。如果需要防止Idle线程被终止,可以将Idle时间设 置为无限大,具体如下:

setKeepAliveTime(Long.MAX_VALUE,TimeUnit.NANOSECONDS);

​ 默认情况下,Idle超时策略仅适用于存在超过corePoolSize线程 的情况。但若调用了allowCoreThreadTimeOut(boolean)方法,并且传 入了参数true,则keepAliveTime参数所设置的Idle超时策略也将被应 用于核心线程。

线程工厂(ThreadFactory)

​ ThreadFactory是Java线程工厂接口,这是一个非常简单的接口, 具体如下:

public interface ThreadFactory {
    Thread newThread(Runnable r);//唯一的方法:创建一个新线程
}

​ 在调用ThreadFactory的唯一方法newThread()创建新线程时,可 以更改所创建的新线程的名称、线程组、优先级、守护进程状态等。 如果newThread()的返回值为null,表示线程工厂未能成功创建线程, 线程池可能无法执行任何任务。

​ 使用Executors创建新的线程池时,也可以基于 ThreadFactory(线程工厂)创建,在创建新线程池时可以指定将要使 用的ThreadFactory实例。只不过,如果没有指定的话,就会使用 Executors.defaultThreadFactory默认实例。使用默认的线程工厂实 例所创建的线程全部位于同一个ThreadGroup(线程组)中,具有相同 的NORM_PRIORITY(优先级为5),而且都是非守护进程状态。

自定义一个线程工厂:

public class SimpleThreadFactory implements ThreadFactory{
        static AtomicInteger threadNo = new AtomicInteger(1);
        @Override
        public Thread newThread(Runnable r) {
            String threadName = "simpleThread-" + threadNo.get();
            System.out.println(Thread.currentThread().getName()+"创建一个线程,名称为:"+threadName);
            threadNo.incrementAndGet();
            Thread thread = new Thread(r,threadName);
            thread.setDaemon(true);
            return thread;
        }
}

任务阻塞队列

​ Java中的阻塞队列(BlockingQueue)与普通队列相比有一个重要 的特点:在阻塞队列为空时会阻塞当前线程的元素获取操作。具体来 说,在一个线程从一个空的阻塞队列中获取元素时线程会被阻塞,直 到阻塞队列中有了元素;当队列中有元素后,被阻塞的线程会自动被 唤醒(唤醒过程不需要用户程序干预)。

​ Java线程池使用BlockingQueue实例暂时接收到的异步任务, BlockingQueue是JUC包的一个超级接口,比较常用的实现类有:

  1. ArrayBlockingQueue:是一个数组实现的有界阻塞队列(有 界队列),队列中的元素按FIFO排序。ArrayBlockingQueue在创建时 必须设置大小,接收的任务超出corePoolSize数量时,任务被缓存到 该阻塞队列中,任务缓存的数量只能为创建时设置的大小,若该阻塞 队列已满,则会为新的任务创建线程,直到线程池中的线程总数大于 maximumPoolSize。

  2. LinkedBlockingQueue:是一个基于链表实现的阻塞队列, 按FIFO排序任务,可以设置容量(有界队列),不设置容量则默认使 用Integer.Max_VALUE作为容量(无界队列)。该队列的吞吐量高于 ArrayBlockingQueue。

    ​ 如果不设置LinkedBlockingQueue的容量(无界队列),当接收的 任务数量超出corePoolSize时,则新任务可以被无限制地缓存到该阻 塞队列中,直到资源耗尽。有两个快捷创建线程池的工厂方法 Executors.newSingleThreadExecutor和 Executors.newFixedThreadPool使用了这个队列,并且都没有设置容 量(无界队列)。

  3. PriorityBlockingQueue:是具有优先级的无界队列。

  4. DelayQueue:这是一个无界阻塞延迟队列,底层基于 PriorityBlockingQueue实现,队列中每个元素都有过期时间,当从队 列获取元素(元素出队)时,只有已经过期的元素才会出队,队列头 部的元素是过期最快的元素。快捷工厂方法 Executors.newScheduledThreadPool所创建的线程池使用此队列。

  5. SynchronousQueue:(同步队列)是一个不存储元素的阻塞 队列,每个插入操作必须等到另一个线程的调用移除操作,否则插入 操作一直处于阻塞状态,其吞吐量通常高于LinkedBlockingQueue。快 捷工厂方法Executors.newCachedThreadPool所创建的线程池使用此队 列。与前面的队列相比,这个队列比较特殊,它不会保存提交的任 务,而是直接新建一个线程来执行新来的任务。

线程池的拒绝策略

​ 在线程池的任务缓存队列为有界队列(有容量限制的队列)的时 候,如果队列满了,提交任务到线程池的时候就会被拒绝。总体来 说,任务被拒绝有两种情况:

  1. 线程池已经被关闭。
  2. 工作队列已满且maximumPoolSize已满。

​ 无论以上哪种情况任务被拒绝,线程池都会调用 RejectedExecutionHandler实例的rejectedExecution方法。 RejectedExecutionHandler是拒绝策略的接口,JUC为该接口提供了以 下几种实现:

  • AbortPolicy:拒绝策略。使用该策略时,如果线程池队列满了,新任务就会被拒绝,并且 抛出RejectedExecutionException异常。该策略是线程池默认的拒绝 策略。

  • DiscardPolicy:抛弃策略。该策略是AbortPolicy的Silent(安静)版本,如果线程池队列满 了,新任务就会直接被丢掉,并且不会有任何异常抛出。

  • DiscardOldestPolicy:抛弃最老任务策略。也就是说如果队列满了,就会将最早进入队 列的任务抛弃,从队列中腾出空间,再尝试加入队列。因为队列是队 尾进队头出,队头元素是最老的,所以每次都是移除队头元素后再尝 试入队。

  • CallerRunsPolicy:调用者执行策略。在新任务被添加到线程池时,如果添加失败, 那么提交任务线程会自己去执行该任务,不会使用线程池中的线程去 执行新任务。

  • 自定义策略。如果以上拒绝策略都不符合需求,那么可自定义一个拒绝策略, 实现RejectedExecutionHandler接口的rejectedExecution方法即可。

线程池的任务调度流程

​ 线程池的任务调度流程(包含接收新任务和执行下一个任务)大 致如下:

  1. 如果当前工作线程数量小于核心线程数量,执行器总是优先 创建一个任务线程,而不是从线程队列中获取一个空闲线程。
  2. 如果线程池中总的任务数量大于核心线程池数量,新接收的 任务将被加入阻塞队列中,一直到阻塞队列已满。在核心线程池数量 已经用完、阻塞队列没有满的场景下,线程池不会为新任务创建一个 新线程。
  3. 当完成一个任务的执行时,执行器总是优先从阻塞队列中获 取下一个任务,并开始执行,一直到阻塞队列为空,其中所有的缓存 任务被取光。
  4. 在核心线程池数量已经用完、阻塞队列也已经满了的场景 下,如果线程池接收到新的任务,将会为新任务创建一个线程(非核 心线程),并且立即开始执行新任务。
  5. 在核心线程都用完、阻塞队列已满的情况下,一直会创建新 线程去执行新任务,直到池内的线程总数超出maximumPoolSize。如果 线程池的线程总数超过maximumPoolSize,线程池就会拒绝接收任务, 当新任务过来时,会为新任务执行拒绝策略。

总体的线程池调度流程如图:

在这里插入图片描述

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

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

相关文章

数据结构——队列的实现(细就完事了)

1.队列 1.1队列的概念和结构 今天我们要是实现的队列是完全相反的&#xff0c;队列是数据先进先出。而在栈中我们使用的顺序表(数组)来实现的。而队列却用单链表来实现是更加合适的。 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行数据操作的特殊线…

【王道·计算机网络】第六章 应用层【未完】

一、基本概念 1.1 应用层概述 应用层对应用程序的通信提供服务应用层协议定义&#xff1a; 应用进程交换的报文类型&#xff0c;请求还是响应?各种报文类型的语法&#xff0c;如报文中的各个字段及其详细描述字段的语义&#xff0c;即包含在字段中的信息的含义进程何时、如何…

这是关于“树先生“的故事

《数据结构专栏》 文章目录 《数据结构专栏》一、认识树结构如何遍历树如何创建一个树&#xff1f;如何判断一颗树是否是完全二叉树&#xff1f; 二、树的简单算法——递归1.相同树2.镜像树3.单值二叉树 总结 一、认识树结构 树的定义&#xff1a;树是指由N&#xff08;N>0…

高效研发团队都在看!一套方法论带你找到适合自己的效能提升路径

近日&#xff0c;ONES 受邀参加 2023 QECon 全球软件质量&效能大会&#xff08;深圳站&#xff09;。在会上&#xff0c;ONES 研发效能改进咨询顾问陈仪&#xff0c;发表了主题为《如何为研发团队打造专属的效能提升路径》的演讲。 陈仪有着丰富的咨询经验&#xff0c;曾带…

Netty核心技术二--BIO编程

1. I/O模型 I/O 模型简单的理解&#xff1a;就是用什么样的通道进行数据的发送和接收&#xff0c;很大程度上决定了程序通信的性能 Java共支持3种网络编程模型/IO模式&#xff1a;BIO、NIO、AIO Java BIO &#xff1a;同步并阻塞(传统阻塞型)&#xff0c;服务器实现模式为一个…

C++每日一练:饿龙咆哮-逃离城堡(避坑指南)非负整数求和

文章目录 前言一、题目二、解题代码及思路1、思路2、代码 三、非负整数求和总结 前言 饿龙这一题要说难度嘛&#xff0c;还真是挺简单的&#xff0c;但要满分也是有坑的&#xff01;本文就记录了笔者解题过程&#xff0c;希望能对读者使用C编程有所启发。至于非负整数求和代码…

RocketMQ集群环境部署

文章目录 1. 准备环境2. 修改主机名3. 免密登录配置4. 配置RocketMQ集群5. 搭建RocketMQ集群6. 启动集群 1. 准备环境 准备好三台虚拟机&#xff0c;下面是我的虚拟机的一些基本信息 名称ip地址worker010.117.33.135worker110.117.39.202worker210.117.9.52 三台虚拟机都已经…

Windows下nginx的配置与启动

一&#xff0c;下载 http://nginx.org/&#xff0c;打开官网&#xff0c;点击download 选择下载稳定版 二&#xff0c;解压 1&#xff0c;解压到硬盘某个目录 2&#xff0c;由于80端口被占用&#xff0c;于是我要修改conf目录下的nginx.conf文件 查看端口是否被占用 net…

路径规划算法:基于蝴蝶算法的路径规划算法- 附代码

路径规划算法&#xff1a;基于蝴蝶优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于蝴蝶优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法蝴蝶…

【C++】类和对象(上):带你速度了解什么是类,如何定义类!!

前言&#xff1a; 前面我们学习C一些基础的内容&#xff0c;也可以说C针对C语言的缺陷进行改进。而今天我们要学的是C的内容也就是类和对象。 一、初识类&#xff1a; 我们先来看看C语言解决一个问题的过程&#xff1a; 假设有以下这个场景&#xff1a;你需要手洗一件衣服&am…

算法Day09 | KMP,28. 实现 strStr() ,459.重复的子字符串

Day09 KMP28. 实现 strStr()459.重复的子字符串 KMP KMP是三个人人名缩写&#xff0c;用于在文本字符串text中搜索pattern字符串&#xff0c;返回在text中第一出现的位置。 算法做法就是在暴力匹配的基础上加速匹配。通过对pattern字符串求next数组(该数组也成为前缀表)&#…

element ui 表格内嵌图片预览展示样式问题 (element plus)

❤️砥砺前行&#xff0c;不负余光&#xff0c;永远在路上❤️ 目录 前言一、问题二、解决 前言 一、问题 二、解决 添加上preview-teleported 属性即可。

ChatGPT ✖️ 前端 = 有点er意思

HOT! HOT! HOT! &#x1f525; &#x1f525; &#x1f525; ChatGPT登上了国内各大平台的热搜榜&#xff0c;应该在去年11月末的时候就有不少同学了解并使用过&#xff0c;那个时候它刚刚问世&#xff0c;在互联网圈子里有了很大的热度&#xff0c;但是对于大众来说&#xff…

OpenGLES读写图像数据(内存与GPU)——使用PBO

一、什么是PBO 在 OpenGL 开发中&#xff0c;特别是在低端平台上处理高分辨率的图像时&#xff0c;图像数据在内存和显存之前拷贝往往会造成性能瓶颈&#xff0c;而利用 PBO 可以在一定程度上解决这个问题。 PBO &#xff08;Pixel Buffer Object&#xff09;是 OpenGL ES 3.…

iOS开发提效cocoapods插件cocoapods-util

cocoapods-util介绍 cocoapods-util是一个iOS开发提效的cocoapods插件。 取名util的原因是我想做一个通用的插件&#xff0c;把一些iOS中常用的命令或问题整理起来。 插件中除了package命令是根据cocoapods-packager插件做了修改而来&#xff0c;其余命令都是属于自己总结开…

uwb高精度定位系统源码 UWB高精度定位技术原理与实现

uwb高精度定位系统 UWB高精度定位技术原理与实现 近些年物联网产业高速发展&#xff0c;越来越多的物联网终端连上了网络&#xff0c;实现了人与物&#xff0c;甚至物与物之间的互连互通。随着智能化要求的进一步提高和物联网应用的进一步拓展&#xff0c;除了互联互通&#x…

Doxygen源码分析:doxygen执行过程的拆解

Doxygen源码分析&#xff1a;doxygen执行过程的拆解 2023-05-19 23:09:17 ~ 2023-05-20 16:38:13 ChrisZZ imzhuofoxmailcom Hompage https://github.com/zchrissirhcz 文章目录 Doxygen源码分析&#xff1a;doxygen执行过程的拆解1. doxygen 版本2. doxygen 可执行程序的入口…

System V方案 — 共享内存

目录 System V方案 — 详述共享内存共享内存共享内存的原理共享内存数据结构共享内存函数实例 消息队列消息队列数据结构消息队列函数创建删除 信号量信号量数据结构信号量函数创建删除 进程互斥 总结 System V方案 — 详述共享内存 SystemV标准的进程间通信方式&#xff0c;是…

【react 全家桶】react-Hook(上)

本人大二学生一枚&#xff0c;热爱前端&#xff0c;欢迎来交流学习哦&#xff0c;一起来学习吧。 <专栏推荐> &#x1f525;&#xff1a;js专栏 &#x1f525;&#xff1a;vue专栏 &#x1f525;&#xff1a;react专栏 文章目录 14【react-Hook &#xff08;上&#x…