当前位置: 首页 > news >正文

【Java多线程学习7】Java线程池技术

线程池技术

一、什么是线程池

线程池顾名思义是管理一组线程的池子。当有任务要处理时,直接从线程池中获取线程来处理,处理完之后线程不会立即销毁,而是等待下一个任务。

二、为什么要使用线程池? 线程池的作用?

  • 1、降低资源消耗:通过重复利用已创建的线程,降低线程创建和销毁造成的消耗。
  • 2、提高响应速度:当任务到达时,任务可以不需要等待线程的创建,就立即执行。
  • 3、提高了线程的可管理性:通过创建线程池可以对线程进行统一的分配、调优和监控。

三、如何创建线程池

方法一:通过ThreadPoolExecutor构造函数来创建线程池(推荐)。

public class TaskExecutor {//线程池核心线程数量public static final int CORE_POOL_SIZE = 10;//线程池最大线程数public static final int MAX_POOL_SIZE = 30;//当线程数大于核心线程数时,多余的空闲线程存活的最长时间public static final int KEEP_ALIVE_TIME = 100;//线程池等待队列public static final int BLOCKING_QUEUE_SIZE = 10000;//通过ThreadPoolExecutor构造函数来创建线程池public static final ThreadPoolExecutor PROCESS_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE,MAX_POOL_SIZE,KEEP_ALIVE_TIME,TimeUnit.MICROSECONDS,new LinkedBlockingDeque<Runnable>(BLOCKING_QUEUE_SIZE),new ThreadFactoryBuilder().setNameFormat("thread-pool-%d").build());public static void executor(Runnable task) {PROCESS_EXECUTOR.execute(task);}
}

方法二:通过Executor框架的工具类Executors来创建线程池

public class TaskExecuter {public static void main(String[] args) {//通过Executors工具类来创建线程池ExecutorService threadPool = Executors.newFixedThreadPool(10);threadPool.execute(() -> {System.out.println("Hello wys");});}}

问题思考:
在实际的开发中为什么推荐使用通过ThreadPoolExecutor构造函数的方式来创建线程池,而不推荐使用Executors工具类的方式来创建线程池呢?
答:
通过 ThreadPoolExecutor 构造函数的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险,因为我们通过使用ThreadPoolExecutor构造函数的方式去创建线程的话,可以指定线程池的核心参数,如:线程池核心线程的数量、线程池最大线程的数量、空闲线程的存活时间、任务队列、拒绝策略等,这样便于对线程池更好的统一的去管理,避免了资源耗尽的风险

另外,Executors 返回线程池对象的弊端如下:

  • FixedThreadPool 和 SingleThreadExecutor:使用的是无界的 LinkedBlockingQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。
  • CachedThreadPool:使用的是同步队列 SynchronousQueue, 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。
  • ScheduledThreadPool 和 SingleThreadScheduledExecutor : 使用的无界的延迟阻塞队列DelayedWorkQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。

在这里插入图片描述

四、线程池核心参数

我们通过查看ThreadPoolExecutor构造函数源码来了解下线程池核心参数

    public ThreadPoolExecutor(int corePoolSize, //线程池核心线程的数量int maximumPoolSize, //线程池最大线程数量long keepAliveTime, //当线程数量大于核心线程数量时,多余的空闲线程存活的最长时间TimeUnit unit, //时间单位BlockingQueue<Runnable> workQueue, //任务队列,用来存储等待执行任务的队列ThreadFactory threadFactory, //线程工厂,用来创建线程,一般默认即可RejectedExecutionHandler handler //拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制拒接策略来处理任务) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.acc = System.getSecurityManager() == null ?null :AccessController.getContext();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;}

ThreadPoolExecutor3个重要参数:

  • corePoolSize : 核心线程的数量。任务队列未达到队列容量时,最大可以同时运行的线程数量。
  • maximumPoolSize : 最大线程数量。 任务队列已满时(任务队列中存放的任务达到队列容量的时候),当前可以同时运行的线程数量变为最大线程数。
  • workQueue任务队列。新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中。

其他常见参数:

  • keepAliveTime:线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁;
  • unit : keepAliveTime 参数的时间单位。
  • threadFactory:executor创建新线程时候会用到。
  • handler:饱和策略。

五、线程的饱和策略

线程的饱和策略handler:如果当前同时允许的线程数量达到最大线程数量,并且任务队列也已经放满了任务时,线程池的饱和策略就会生效,ThreadPoolExecutor定义了一些策略:

  • ThreadPoolExecutor.AbortPolicy:丢弃新任务并抛出异常 (RejectedExecutionException)。AbortPolicy也是线程池默认的拒绝策略
  • ThreadPoolExecutor.CallerRunsPolicy:它不会抛弃任务,也不会抛出异常,而是将任务回退给调用者的线程来执行
  • ThreadPoolExecutor.DiscardPolicy:不处理新任务,直接丢弃。
  • ThreadPoolExecutor.DiscardOldestPolicy:丢弃当前任务队列中存在最久的任务,提交新的任务并执行。

在这里插入图片描述

六、线程池常用的阻塞队列有哪些?

  • ArrayBlockingQueue:是基于数组实现的阻塞队列,在初始化已经开辟好空间,容量是固定的,会存在内存空间浪费和内存碎片的问题

  • LinkedBlockingQueue(无界队列):是基于链表实现的阻塞队列,当创建LinkedBlockingQueue时,可以指定队列容量,也可以不指定队列容量,如果指定了队列容量则为有界队列,如果未指定队列容量则默认为无界队列。如果为无界队列由于队列永远不会被放满(容量为Integer.MAX_VALUE),所以此时最多只能创建核心线程数的线程。

  • SynchronousQueue(同步队列):是一种没有容量的队列,每个插入操作一定要等待一个相应的删除操作(即任务放进队列后,被取出后才能继续放入),目的是保证对于提交的任务,如果有空闲线程,则使用空闲线程来处理;否则新建一个线程来处理任务。

  • DelayedWorkQueue(延迟阻塞队列):其内部元素并不是按照放入的时间排序,而是会按照延迟的时间长短对任务进行排序,内部采用“堆”的数据结构,可以保证每次出对的任务都是当前队列中执行时间最靠前的。

七、线程池处理任务的流程

我们来介绍下,线程池处理任务的流程(即线程池执行execute的流程):
在这里插入图片描述

  • 1、如果当前运行的线程数小于核心线程数,那么当有新的任务到来后会新建一个线程来执行任务(懒加载核心线程)
  • 2、如果当前运行的线程数等于或大于核心线程数,并且线程状态为运行态,那么就把此任务放到任务队列等待执行
  • 3、如果向任务队列投放任务失败(任务队列已经满了),但是当前运行线程数是小于最大线程数的,那么就尝试创建非核心线程来执行任务。
  • 4、如果当前运行的线程数已经等于最大线程,新建线程会使当前运行的线程超出最大线程数,此时会调用饱和策略(RejectedHandler)拒绝当前任务。

八、如何给线程池命名

利用ThreadFactorBuilder来给线程池命名。即new ThreadFactorBuilder().setNameFormat().build();

ThreadFactory 接口是 Java 提供的一个创建线程的工厂接口,我们可以通过实现这个接口来定制线程的行为。ThreadFactory 接口里唯一的方法是 newThread(Runnable r),它会创建并返回一个新的线程对象。
在实现该接口时,可以通过重载 newThread() 方法来定制线程的名称。具体实现方式示例代码如下:

ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("my-thread-pool-%d").build();
ExecutorService executorService = new ThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(), namedThreadFactory);

九、如何设定线程池的大小

(一)背景

我们在上面的学习中了解到使用线程池技术可以提高任务的响应速度,那是不是就说明线程池中线程的数量越大越好呢?其实理性的人都知道并不是这样的,虽然我们可以将线程池的数量设定的很大,但处理任务的CPU资源是不变的,这样就会导致大量的上下文切换,从而增加线程的执行时间,影响了整体执行效率

(二)上下文切换

问题1:什么是上下文切换
1、上下文切换是指:在多线程环境下,CPU从一个线程切换到另一个线程时,保存当前线程的上下文信息,并加载另一个线程上下文信息的过程。
上下文信息指:线程在执行过程中自己的运行条件和状态,比如,程序计数器、栈信息、寄存器的值等(寄存器是指CPU内部一组高速存储单元,用于临时存储和操作数据)

2、多线程编程中一般线程的个数都大于 CPU 核的个数,而一个 CPU 核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU 采取的策略是为每个线程分配时间片并轮转的形式。当一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用,这个过程就属于一次上下文切换。概括来说就是:当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态。任务从保存到再加载的过程就是一次上下文切换。上下文切换通常是计算密集型的。也就是说,它需要相当可观的处理器时间,在每秒几十上百次的切换中,每次切换都需要纳秒量级的时间。所以,上下文切换对系统来说意味着消耗大量的 CPU 时间,事实上,可能是操作系统中时间消耗最大的操作。

(三)如何设定线程池的大小

  • CPU 密集型任务(线程池设定大小N+1): 这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1。比 CPU核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。
  • I/O 密集型任务(线程池设定2N或是更大): 这种任务应用起来,系统会用大部分的时间来处理 I/O 交互(即CPU大部分时间是空闲),而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N。

相关文章:

【Java多线程学习7】Java线程池技术

线程池技术 一、什么是线程池 线程池顾名思义是管理一组线程的池子。当有任务要处理时&#xff0c;直接从线程池中获取线程来处理&#xff0c;处理完之后线程不会立即销毁&#xff0c;而是等待下一个任务。 二、为什么要使用线程池? 线程池的作用&#xff1f; 1、降低资源…...

VMware虚拟机NAT模式Ubuntu无法上网解决方案

发现只要NAT模式&#xff0c;ping地址时就报网络不可达&#xff0c;且右上方网络图标消失&#xff0c;但是外部USB网络设备又只能在NAT模式下使用。。。 博主的解决方案如下&#xff1a; 按WinR键入services.msc&#xff0c; 找到VMware DHCP Service、VMware NAT Service和V…...

Linux中无法忘记mysql密码处理办法

找到/etc/my.cnf或者/etc/mysql/my.cnf文件 添加下面两行代码&#xff0c;取消密码验证 [mysqld] skip-grant-table使用命令登录&#xff1a;mysql -u root -p&#xff0c;回车&#xff0c;回车使用sql语句来修改密码 mysql>use mysql; mysql>update user set password…...

vue 使用 el-upload 上传文件(自动上传/手动上传)

vue 使用 el-upload 上传文件(自动上传/手动上传) 文章目录 1. 自动上传(选择完文件后,调用axios上传)2.手动上传 1. 自动上传(选择完文件后,调用axios上传) <el-uploadref"upload1"action:multiple"false"accept".xlsx,.csv,.xls":auto-upl…...

服务器遭受攻击之后的常见思路

哈喽大家好&#xff0c;我是咸鱼 不知道大家有没有看过这么一部电影&#xff1a; 这部电影讲述了男主是一个电脑极客&#xff0c;在计算机方面有着不可思议的天赋&#xff0c;男主所在的黑客组织凭借着超高的黑客技术去入侵各种国家机构的系统&#xff0c;并引起了德国秘密警察…...

C语言学习笔记 使用vscode外部console出现闪退-12

前言 在使用vscode的外部console时&#xff0c;会出现闪退现象&#xff0c;这是因为程序运行结束后&#xff0c;系统自动退出了终端&#xff08;终端机制决定的&#xff09;。我们可以在C程序结束后&#xff0c;使用system函数来暂停DOS终端系统&#xff0c;这样就可以完整地看…...

从Spring源码看Spring如何解决循环引用的问题

Spring如何解决循环引用的问题 关于循环引用&#xff0c;首先说一个结论&#xff1a; Spring能够解决的情况为&#xff1a;两个对象都是单实例、且通过set方法进行注入。 两个对象都是单实例&#xff0c;通过构造方法进行注入&#xff0c;Spring不能进行循环引用问题&#x…...

03 - 通过git log可以查看版本演变历史

通过git log可以查看版本演变历史 主要包括&#xff1a; commit 哈希id提交的Author信息提交的日期和时间commit info信息 git log本人常用&#xff0c;显示简洁&#xff1a; git log --oneline当log条数很多的时候&#xff0c;可以如下指定显示的数量&#xff1a; git log…...

【图论】单源最短路

算法提高课笔记。&#xff08;本篇还未更新完… 目录 单源最短路的建图方式例题热浪题意思路代码 信使题意思路代码 香甜的黄油题意思路代码 最小花费题意思路代码 最优乘车题意思路代码 昂贵的聘礼题意思路代码 单源最短路的建图方式 最短路问题可以分为以下两类&#xff1a…...

闻道网络:2023宠物消费网络营销洞察数据报告(附下载)

关于报告的所有内容&#xff0c;公众【营销人星球】获取下载查看 核心观点 行业持续升级&#xff0c;增速放缓&#xff0c;正朝着多元化和专业化的方向发展&#xff1b;自公共事件以来&#xff0c;因&#xff0c;“猫不用遛”&#xff0c;养猫人士增速迅猛反超犬主人&#xf…...

Docker 安装和架构说明

Docker 并非是一个通用的容器工具&#xff0c;它依赖于已存在并运行的Linux内核环境。 Docker实质上是在已经运行的Liunx下制造了一个隔离的文件环境&#xff0c;因此他的执行效率几乎等同于所部署的linux主机。因此Docker必须部署在Linux内核系统上。如果其他系统想部署Docke…...

101. 对称二叉树

题目 原题链接 : 101.对称二叉树 题面 : 对于这一题呢&#xff0c;题目要求给出递归和迭代两种方式来解决!!! 注 : 这一题不仅仅是判断左右两个子节点是否对称,而是要遍历两棵树而且要比较内侧和外侧节点 递归 先确认递归三要素 : 确定递归函数的参数和返回值 bool …...

cmake应用:集成gtest进行单元测试

编写代码有bug是很正常的&#xff0c;通过编写完备的单元测试&#xff0c;可以及时发现问题&#xff0c;并且在后续的代码改进中持续观测是否引入了新的bug。对于追求质量的程序员&#xff0c;为自己的代码编写全面的单元测试是必备的基础技能&#xff0c;在编写单元测试的时候…...

静态时序分析与时序约束

一、时序分析的基本概念 1. 时钟 理性的时钟模型是一个占空比为50%且周期固定的方波&#xff1a; 实际电路中输入给FPGA的晶振时钟信号是正弦波&#xff1a; 2. 时钟抖动 Clock Jitter&#xff0c;时钟抖动&#xff0c;相对于理想时钟沿&#xff0c;实际时钟存在不随时钟存在…...

YOLOv5基础知识入门(3)— 目标检测相关知识点

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。YOLO算法发展历程和YOLOv5核心基础知识学习完成之后&#xff0c;接下来我们就需要学习目标检测相关知识了。为了让大家后面可以顺利地用YOLOv5进行目标检测实战&#xff0c;本节课就带领大家学习一下目标检测的基础知识点&…...

10个AI绘图生成器让绘画更简单

AI不仅影响商业和医疗保健等行业&#xff0c;还在创意产业中发挥着越来越大的作用&#xff0c;开创了AI绘画生成器新时代。在绘画领域当然也是如此&#xff0c;与传统的绘画工具不同&#xff0c;AI人工智能时代的绘画工具是全自动的、智能的&#xff0c;甚至可以说是“傻瓜式”…...

干货满满的Python知识,学会这些你也能成为大牛

目录 1. 爬取网站数据 2. 数据清洗与处理 3. 数据可视化 4. 机器学习模型训练 5. 深度学习模型训练 6. 总结 1. 爬取网站数据 在我们的Python中呢&#xff0c;使用爬虫可以轻松地获取网站的数据。可以使用urllib、requests、BeautifulSoup等库进行数据爬取和处理。以下是…...

【Leetcode】155. 最小栈、JZ31 栈的压入、弹出序列

作者&#xff1a;小卢 专栏&#xff1a;《Leetcode》 喜欢的话&#xff1a;世间因为少年的挺身而出&#xff0c;而更加瑰丽。 ——《人民日报》 155. 最小栈 155. 最小栈 题目描述; 设计一个支持 push &#xff0c;pop &#xff0c;top …...

网络安全(黑客技术)自学笔记

一、什么是网络安全&#xff1f;什么是黑客&#xff1f; 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等…...

iOS学习—制作全局遮罩

在.h文件中线声明show()方法 - (void)show; .m文件中添加全屏遮罩&#xff0c;在遮罩上添加了一个选择框并添加了底部弹出的动画&#xff0c;可自行在其中添加tableview、pickerview等其他视图&#xff0c;并添加了点击选择框视图外区域隐藏 #import "MaskView.h"…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

MySQL用户和授权

开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务&#xff1a; test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

windows系统MySQL安装文档

概览&#xff1a;本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容&#xff0c;为学习者提供全面的操作指导。关键要点包括&#xff1a; 解压 &#xff1a;下载完成后解压压缩包&#xff0c;得到MySQL 8.…...

MyBatis中关于缓存的理解

MyBatis缓存 MyBatis系统当中默认定义两级缓存&#xff1a;一级缓存、二级缓存 默认情况下&#xff0c;只有一级缓存开启&#xff08;sqlSession级别的缓存&#xff09;二级缓存需要手动开启配置&#xff0c;需要局域namespace级别的缓存 一级缓存&#xff08;本地缓存&#…...