【JavaEE】线程池

目录

前言

什么是线程池

线程池的优点

ThreadPollExecutor中的构造方法

corePoolSize && maximumPoolSize

keepAliveTime && unit

workQueue

 threadFactory

如何在java中使用线程池

1.创建线程池对象

 2.调用submit添加任务

3.调用shutdown关闭线程池

手动实现线程池

创建一个线程池类

测试


前言

在前面我们都是通过new Thread() 来创建线程的,虽然在java中对线程的创建、中断、销毁、等值等功能提供了支持,但从操作系统角度来看,频繁的创建和销毁线程,是需要大量的时间和资源的。那么我们能不能先把线程提前从系统中申请好,需要使用线程的时候,直接从这个地方取出来,而不是从系统中重新申请,等线程用完之后,也是放回到这个地方,而不用频繁的进行创建和销毁呢?那我们就需要用到线程池,线程池能够提高程序的效率

什么是线程池

线程池 (ThreadPool) 是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建后保持活动的线程中执行这些任务。线程池的主要目的是减少创建和销毁线程的开销,提高响应速度和整体性能。

为什么说线程池里取线程,比从系统中创建一个线程效率要高呢?

我们从线程池里直接取出线程是纯用户态操作,而我们新建一个线程是内核态+用户态配合完成的。

内核态:指操作系统内核及其相关组件(如设备驱动程序)运行的状态。

用户态:指用户程序(如应用程序和服务)运行的状态。

操作系统=操作系统内核+操作系统配套的应用程序

假设现在要去银行办银行卡,那么银行内部人员就是内核态,前往办卡的就为用户态。

如果我们想要办卡,就走到小窗口向柜员说明情况,此时柜员就会帮你进行操作。但如果此过柜员问你要身份证复印件,如果没有,那么就需要去进行打印。

若请求柜员帮你进行打印,那么柜员什么时候打印,这个是不确定的,效率较低。这种就是用户态+内核态,调用系统API,由系统内核来完成这一系列操作。

若我们自己去自助复印机进行复印,整个过程就是连贯可控的,效率较高。这种就是纯用户态,由我们自己控制。

线程池的优点

  • 减少资源消耗:通过重复利用已创建的线程,减少了创建和销毁线程所需的资源开销。
  • 提高响应速度:当有新的任务到达时,若线程池中有空闲的线程,任务可以直接执行,无需等待新的线程创建。
  • 防止资源耗尽:通过限制线程池中的最大线程数,可以避免因创建过多线程而引起的资源耗尽问题。
  • 提高线程利用率:线程池中的线程可以被重复利用。

线程池最大的好处就是减少每次启动、销毁线程的损耗。

ThreadPollExecutor中的构造方法

在java中,给我们提供了现成的线程池供我们来使用。  

我们可以看一下标准库中的线程池,在java.util.concurrent中给我们提供线程池的相关方法。

在点开concurrent之后,我们在接口中往下划,找到ThreadPoolExecutor类。我们可以看到,ThreadPoolExecutor类的构造方法有着好几个参数,那么这些参数都是什么意思呢?

‘’

 我们拿第四个构造方法来看。

corePoolSize && maximumPoolSize

corePoolSize:即核心线程数

maximumPoolSize:最大核心线程数

线程池可以支持“线程扩容”,若某个线程池初始状态下有m个线程,但m若不够用,就会自动增加m的个数。

在java标准库的线程池中,把线程分为两类:核心线程和非核心线程

核心线程可以理解为最少有多少个线程,而非核心线程就是线程扩容新增的线程。

核心线程数会始终存在线程池内部,而非核心线程,在繁忙的时候就会被创建出来,在空闲时就会把这些线程释放掉。

最大核心线程数=核心线程数+非核心线程数的最大值

keepAliveTime && unit

 那么非核心线程在空闲的时候就会被立即释放掉吗?

若在空闲一会后,又有新的任务,那岂不是要重新新增线程。

所以在构造方法中,给我们提供了两个参数,用来设置非核心线程在空闲时等待任务的最长时间。

keepAliveTime:表示线程池中空闲的非核心线程在终止前等待新任务的最长时间。

unit:用来设置keepAliveTime参数的时间单位。

workQueue

我们可以看到workQueue的类型为BlockingQueue<Runnable>,说明这是个用来存放任务的阻塞队列。可以根据需要设置阻塞队列的类型,如果需要优先级,则可以使用PriorityBlockingQueue;如果不需要优先级且任务的数目是恒定的,则可以使用ArrayBlockingQueue;如果任务的数目不是恒定的,则可以使用LinkedBlockingQueue

作用:

  1. 任务缓存:当线程池中的线程都在执行任务时,新提交的任务会被暂时存储在工作队列中,直到有空闲线程来执行它们。
  2. 任务调度:工作队列可以帮助线程池按照一定的策略来调度任务的执行顺序。
  3. 资源管理:通过限制工作队列的大小,可以控制线程池中等待执行的任务数量,从而避免资源耗尽的情况。

 threadFactory

 threadFactory:创建线程的⼯⼚,参与具体的创建线程⼯作.通过不同线程⼯⼚创建出的线程相当于 对⼀些属性进⾏了不同的初始化设置。

*handler(类型RejectedExecutionHandler)

最后一个参数是有关队列满后不要进行阻塞,而是要进行拒绝了,我们可以在构造方法上面查看线程池的拒绝策略有哪些.

在java提供了4种拒绝策略

  1. ThreadPoolExecutor.AbortPolicy:有新的任务想要入队时,直接抛出异常
  2. ThreadPoolExecutor.CallerRunsPolicy:新添加的任务线程池拒绝执行,由添加任务的线程执行该任务
  3. ThreadPoolExecutor.DiscardOldestPolicy:丢弃掉最旧的未被处理的请求,执行新的任务
  4. ThreadPoolExecutor.DiscardPolicy:丢弃掉当前新加的任务,不执行

讲完了线程池中的构造方法以及拒绝策略,那么如何在java中来使用线程池呢?

如何在java中使用线程池

1.创建线程池对象

         ExecutorService ex=Executors.newFixedThreadPool(4);

ExecutorService是一个继承于Executor的接口,主要用来管理和控制线程,是Java并发编程的重要工具。

在java标准库中,虽然ThreadPoolExecutor功能强大,但是使用起来比较麻烦,所以java标准库对这个类进行了封装:Executors类

 Executors类Java中用于创建线程池的工厂类,它提供了一系列的静态工厂方法,用于创建不同类型的线程池。

我们可以看到Executors工厂类中的工厂方法有以下几种类型:

  1. newCachedThreadPool()创建一个可缓存的线程池。这个线程池的线程数量可以根据需要自动扩展,如果有可用的空闲线程,就会重用它们;如果没有可用的线程,就会创建一个新线程。适用于执行大量的短期异步任务。
  2. newFixedThreadPool(int nThreads)创建一个固定大小的线程池,其中包含指定数量的线程。线程数量是固定的,不会自动扩展。适用于执行固定数量的长期任务。
  3. newSingleThreadExecutor()创建一个单线程的线程池。这个线程池中只包含一个线程,用于串行执行任务。适用于需要按顺序执行任务的场景。
  4. newScheduledThreadPool(int corePoolSize)创建一个固定大小的线程池,用于定时执行任务。线程数量固定,不会自动扩展此线程池;可以安排在给定延迟后运行命令或者定期执行。
  5. newSingleThreadScheduledExecutor()创建一个单线程的定时执行线程池。只包含一个线程,用于串行定时执行任务。
  6. newWorkStealingPool(int parallelism)创建一个工作窃取线程池,线程数量根据CPU核心数动态调整。这种线程池适合处理大量细粒度的可取消任务。

 2.调用submit添加任务

这里我们创建的是一个固定线程数为4的线程池,在线程池中,我们想要添加任务需要调用submit()方法,可以看到需要的参数是一个Runnable类型的任务。

 我们可以使用Lambda表达式或者直接new Runnable。(这里用第一个)

         for(int i=0;i<100;i++) {int id = i;ex.submit(() -> {System.out.println(Thread.currentThread().getName() + "  任务:" + id);});}

当我们运行之后,会发现任务都结束后,程序还没有停止,这是为什么呢?

在 Java 中,线程池创建的线程默认为前台线程,虽然main线程结束了,但是线程池里的前台线程仍然存在。

3.调用shutdown关闭线程池

如果我们想要关闭线程池中的线程,我们可以使用shutdown方法。

  public static void main(String[] args) {ExecutorService ex=Executors.newFixedThreadPool(4);for(int i=0;i<100;i++) {int id = i;ex.submit(() -> {System.out.println(Thread.currentThread().getName() + "  任务:" + id);});}ex.shutdown();}

既然我们学会如何使用java中的线程池,那么我们可以来实现一个线程池.

手动实现线程池

创建一个线程池类

由于这里是使用的阻塞队列,在队列满的时候若添加新的任务,会进入阻塞等待状态,不同于java线程池里的拒绝策略。

/*** 自定义线程池执行器类* 该类通过实现一个具有固定大小的线程池和一个阻塞队列来管理线程,用于异步执行任务*/
class MyThreadPoolExecutor {// 创建阻塞队列,用于存放待执行的任务// 队列大小设为1000,用于控制并发任务的数量,避免过多任务导致资源耗尽BlockingQueue<Runnable>  blockingQueue=new ArrayBlockingQueue<>(1000);/*** 构造函数,初始化线程池* 创建一个线程,该线程循环从阻塞队列中取任务并执行* 这个线程是线程池中的工作线程,负责执行提交的任务*/public MyThreadPoolExecutor(int n) {for (int i = 1; i <= n; i++) {Thread t = new Thread(() -> {// 无限循环,确保线程池可以持续处理任务,直到程序中断或阻塞队列被清空while (true) {try {// 从阻塞队列中取出一个任务,如果队列为空,则线程被阻塞,直到有任务放入队列Runnable task = blockingQueue.take();// 执行取出的任务task.run();} catch (InterruptedException e) {// 如果线程在等待状态时被中断,抛出运行时异常// 这通常会导致程序异常终止throw new RuntimeException(e);}}});// 启动线程池中的工作线程t.start();}}/*** 提交一个任务到线程池* @param task 需要被执行的任务*            任务被放入阻塞队列中,随后由线程池中的工作线程执行*/public void submit(Runnable task){// 将任务放入阻塞队列,如果队列已满,则操作会阻塞,直到有空间可用blockingQueue.offer(task);}
}

测试

class DemoTest1{public static void main(String[] args) throws InterruptedException {MyThreadPoolExecutor ex=new MyThreadPoolExecutor(4);for(int i=0;i<100;i++) {int id = i;ex.submit(()->{System.out.println(Thread.currentThread().getName()+"  任务:"+id);});}}
}


本篇就先到这了~
若有不足,欢迎指正~

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

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

相关文章

【Python】requests的response.text 和 urllib.request 的 response.read()的区别

刚写代码的时候&#xff0c;我经常会把requests 和 urllib下的request 包搞混&#xff0c;这两个请求响应的方法看起来很相似&#xff0c;但是写获取的方法是不一样的。 前者requests 是用response.text 来获取源码&#xff0c;而 urllib.request是用 response.read() 来获取h…

数学建模--智能算法之免疫算法

目录 基本原理 应用实例 代码示例 总结 免疫算法在免疫系统研究中的应用和进展是什么&#xff1f; 如何量化评估免疫算法在不同优化问题中的性能和效率&#xff1f; 免疫算法与其他智能优化算法&#xff08;如遗传算法、粒子群优化&#xff09;相比有哪些独特优势和局限性…

【C++】C++11的新特性 — 线程库 ,原子操作 , 条件变量

勇敢就是接受发生在你身上的事&#xff0c;并把它尽力做到最好。 -- 约翰・欧文 -- C11的新特性 1 线程1.1 线程概念1.2 C中的线程1.3 线程并行1.4 锁 2 原子操作3 条件变量Thanks♪(&#xff65;ω&#xff65;)&#xff89;谢谢阅读&#xff01;&#xff01;&#xff01;下…

编译和汇编的区别

一、编译 编译是将高级语言&#xff08;如C、C、Java等&#xff09;编写的源代码转换成计算机可以直接执行的低级语言&#xff08;通常是机器语言或汇编语言&#xff09;的过程 编译 —— 将人类可读的源代码转换为计算机可执行的指令集 编译过程 通常包括词法分析、语法分…

正点原子imx6ull-mini-Linux驱动之Linux 网络驱动实验

网络驱动是 linux 里面驱动三巨头之一&#xff0c;linux 下的网络功能非常强大&#xff0c;嵌入式 linux 中也常 常用到网络功能。前面我们已经讲过了字符设备驱动和块设备驱动&#xff0c;本章我们就来学习一下 linux 里面的网络设备驱动。 1&#xff1a;嵌入式网络简介 1.1…

如何给源代码加密?这款加密软件教给你操作步骤

给源代码加密是保护软件知识产权和商业机密的重要手段。以这款加密软件安企神为例&#xff0c;给源代码加密的过程可以概述为以下几个方面。 一、安企神软件概述 安企神是一款功能强大的企业安全加密软件&#xff0c;它提供了全面的源代码防泄漏解决方案。该软件通过透明文件加…

扩散模型系列笔记(一)——DDPM

直观理解 扩散模型分为前向过程&#xff08;扩散过程&#xff0c;Data → \to →Noise&#xff09;和后向过程&#xff08;生成过程或逆扩散过程&#xff0c;Noise → \to →Data&#xff09;。在前向过程中&#xff0c;对于每一个观测样本&#xff0c;不断向样本中添加少量噪…

Leetcode每日刷题之字符串中的第一个唯一字符(C++)

在学习的过程中对代码的熟练运用至关重要&#xff0c;练习解决实际问题就可以很好的锻炼自己的编程能力&#xff0c;接下来让我们练习这道 387.字符串中的第一个唯一字符 思路解析 根据题意我们可以知道这个字符串只有小写字母&#xff0c;并且可能包含多个唯一字符&#xff0…

Java---字符串string练习

目录&#xff1a; 1.将数字转换成罗马数字 2.键盘输入任意字符串&#xff0c;打乱里面的内容 3.返回字符串中最后一个单词长度 4.调整A字符串 看是否可与B字符串匹配 一&#xff1a; //键盘录入一个字符串// 长度小于等于9 只能是数字// -将内容变成罗马数字// Ⅰ Ⅱ Ⅲ Ⅳ…

智慧水务项目(二)django(drf)+angular 18 创建通用model,并对orm常用字段进行说明

一、说明 上一篇文章建立一个最简单的项目&#xff0c;现在我们建立一个公共模型&#xff0c;抽取公共字段&#xff0c;以便于后续模块继承&#xff0c;过程之中会对orm常用字段进行说明&#xff0c;用到的介绍一下 二、创建一个db.py 目录如下图 1、代码 from importlib im…

基于QT实现的简易WPS(已开源)

一、开发工具及开源地址&#xff1a; 开发工具&#xff1a;QTCreator &#xff0c;QT 5 开源地址&#xff1a; GitHub - Whale-xh/WPS_official: Simple WPS based on QTSimple WPS based on QT. Contribute to Whale-xh/WPS_official development by creating an acc…

推荐 3个实用且完全免费的在线工具,每天都会用到,无需登录打开即用

100font 100font是一个专业的免费商用字体下载网站&#xff0c;专注于收集、整理和分享各种免费无版权的商用字体。用户可以在这个平台上找到并下载简体中文、繁体中文、英文、日文、韩文等多种语言类型的字体。 该网站的特点包括清晰的分类和直观的下载流程&#xff0c;用户可…

进阶SpringBoot之 Spring 官网或 IDEA 快速构建项目

SpringBoot 就是一个 JavaWeb 的开发框架&#xff0c;约定大于配置 程序 数据结构 算法 微服务架构是把每个功能元素独立出来&#xff0c;再动态组合&#xff0c;是对功能元素的复制 这样做可以节省调用资源&#xff0c;每个功能元素的服务都是一个可替代、可独立升级的软…

算法混合杂项

基础类型 可用template 投影 是有方向的 求俩直线交点 推公式 q我们不知道&#xff0c;已知p1 p2&#xff0c;正弦定理&#xff0c;α可以用叉积表示出来 β同理 所以我们能求出p1q 已知piq 回归到我们上一个问题&#xff0c;已知方向和长度&#xff0c;我们就能够求出Voq …

C语言 ——— 学习并使用字符分类函数

目录 学习isupper函数 学习isdigit函数 学习tolower函数 将输入的字符串中把大写字母转换为小写字母并输出 学习isupper函数 参数部分&#xff1a; 形参需要传递的是一个字母&#xff0c;字符在ASCII码表上是以整型存储的&#xff0c;所以实参部分用(int c)没有问题 返回…

【iOS】AutoreleasePool自动释放池的实现原理

目录 ARC与MRC项目中的main函数自动释放池autoreleasepool {}实现原理AutoreleasePoolPage总结 objc_autoreleasePoolPush的源码分析autoreleaseNewPageautoreleaseFullPageautoreleaseNoPage autoreleaseFast总结 autorelease方法源码分析objc_autoreleasePoolPop的源码分析po…

谁来做引领企业精益变革的舵手最合适?

在这个瞬息万变的商业时代&#xff0c;企业如同航行在波涛汹涌的大海中的巨轮&#xff0c;既需面对未知的挑战&#xff0c;也要抓住稍纵即逝的机遇。而在这场没有终点的航行中&#xff0c;引领企业实现精益变革的舵手&#xff0c;无疑是推动企业破浪前行、稳健致远的关键角色。…

FFmpeg Windows安装教程

一. 下载ffmpeg 进入Download FFmpeg网址&#xff0c;点击下载windows版ffmpeg。 下载第一个essentials版本就行。 二. 环境配置 上面源码解压后如下 将bin添加到系统环境变量 验证安装是否成功&#xff0c;输入ffmpeg –version&#xff0c;显示版本即为安装成功。

Python学习(1):使用Python的Dask库实现并行计算

目录 一、Dask介绍 二、使用说明 安装 三、测试 1、单个文件中实现功能 2、运行多个可执行文件 最近在写并行计算相关部分&#xff0c;用到了python的Dask库。 Dask官网&#xff1a;Dask | Scale the Python tools you love 一、Dask介绍 Dask是一个灵活的并行和分布式…

网工内推 | 国企运维工程师,华为认证优先,最高年薪20w

01 上海陆家嘴物业管理有限公司 &#x1f537;招聘岗位&#xff1a;IT运维工程师 &#x1f537;岗位职责&#xff1a; 1、负责对公司软、硬件系统、周边设备、桌面系统、服务器、网络基础环境运行维护、故障排除。 2、负责对各部门软件操作、网络安全进行检查、指导。 3、负责…