《javaEE篇》--线程池

线程池是什么

线程的诞生是因为进程创建和销毁的成本太大,但是也是相对而言,如果频繁的创建和销毁线程那么这个成本就不能忽略了。

一般有两种方法来进一步提高效率,一种是协程(这里不多做讨论),另一种就是线程池

假如说有一个学校食堂窗口的老板想到给学生提供外卖服务,而且有一个奇妙的想法,每当需要送外卖就现场雇一个学生来送,然后解雇。(就相当于平时的有一个任务就创建一个线程来处理),但是老板还是觉得频繁的雇佣和解雇学生的成本太大,于是老板又有一个点子。指定一个指标,外卖员的人数扩张到3个人,但还是随着外卖的数量逐步雇人。于是再有外卖来了老板就看,如果外卖员不足3人,就雇一个去送,若有3个人了,就先把外卖放到一边,等3个外卖员空闲时在送。这样的方法就类似于线程池。

 这样把线程创建好,放在“池子”里,后续用的时候直接从池子里取就好,不用系统进行创建,不用时还是放到池子里,不用系统销毁。那么为什么从池子取的效率就比创建新线程高?因为从池子取这个动作,是纯用户态的操作,而创建新的线程,这个动作则是,需要用户态+内核态互相配合。

线程池的优势

  • 线程池最大的好处就是减少每次启动、销毁线程的损耗。
  • 当有任务来时,不需要等待新线程的创建,利用已创建的线程就可以执行
  • 方便对线程进行统一管理和调度

工厂模式

线程池对象不是我们直接new的,而是专门通过一个方法,返回一个线程池对象,这种设计模式,就叫做工厂模式。

我们通常创建对象,使用new关键字。使用new就会触发构造方法,但是构造方法存在一定局限性。很多时候构造一个对象,希望有多种构造方式,多种方式就需要多个版本的构造方法来实现。但是构造方法要求方法的名字必须是类名,不同构造方法,就只能通过重载的方式来区分了。

 实践中,一般单独搞一个类,给这个类搞一些静态方法,由这些静态方法负责构造出对象

线程池的创建

  • 使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池.
  • 返回值类型为 ExecutorService
  • 通过 ExecutorService.submit 可以注册一个任务到线程池中
ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});

 Executors 创建线程池的几种方式

  • newFixedThreadPool: 创建固定线程数的线程池
  • newCachedThreadPool: 创建线程数目动态增长的线程池.(随着往线程池里添加任务,这个线程池中的线程会根据需要自动被创建出来,创建出来之后也不会着急销毁,而是会在池子里保留一定时间,以备随时使用)
  • newSingleThreadExecutor: 创建只包含单个线程的线程池.
  • newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer.

上述几个工厂方法生成的线程池,本质上是 ThreadPoolExecutor 类的封装,这个类功能非常丰富,提供了很多参数,标准库上述的几个工厂方法,其实就是给这个类填写了不同的参数用来构造线程池。

(线程池的真正实现类是ThreadPoolExecutor).

线程池的参数

可见线程池有许多的参数具体如下:

corePoolSize(核心线程数):

线程池中会有一个最小的线程数量(核心线程数),即使这些线程处于空闲状态,也不会被销毁(除非设置了allowCoreThreadTimeOut为true)。当提交一个任务交给线程池后,线程池首先会检查当前线程数是否到达核心线程数,如果没有则创建一个新线程来处理这个任务。

maximumPoolSize(最大线程数):

如果当前线程数已经达到核心线程数,此时继续有任务添加,则会被缓存到工作队列中,如果队列也已经满了,就会创建一个新线程来处理这个任务(不是核心线程),但是线程不会无止尽的创建,最多创建的线程的数量就是有maximumPoolSize定的

keepAliveTime(线程限制超时时长):

如果一个线程处于空闲状态,并且当前线程数大于核心线程数(该空闲线程不是核心线程),那么在一定时间后该非核心线程将会被销毁(如果将allowCoreThreadTimeOut为true,一定时间后,空闲的核心线程也会被销毁)。这个时间就是keepAliveTime

unit(keepAliveTime的单位):

用来指定keepAliveTime的单位的,一般常用的单位有,TimeUnit.MILLISECONDS(毫秒)TimeUnit.SECONDS(秒)TimeUnit.MINUTES(分)。

workQueue(工作队列):

用来存放线程池中的任务的,可以根据需要灵活设置这里的队列是什么。需要优先级,就可以设置PriorityBlockingQueue,不需要优先级而且任务数目相对固定,可以使用ArrayBlockingQueue,如果不需要优先级,并且任务数目变动较大,可以使用LinkedBlockingQueue

threadFactory(线程工厂):

工厂模式的体现,使用threadFactory作为工厂类,由这个类负责创建线程,主要是为了,在创建线程过程中,对线程的属性做出一些修改。可以更方便的创建线程。

handler(线程池的拒绝策略):

一个线程池的任务容量达到上限,继续往线程池里添加任务的时候,会出现什么效果,JDK提供了4种策略

拒绝策略 

当线程池的线程数目达到最大线程数时,所执行的策略。 Executors给我们提供了四种常用的拒绝策略。

  1. ThreadPoolExecutor.AbortPolicy(默认):直接抛出RejectedExecutionException 异常
  2. ThreadPoolExecutor.CallerRunsPolicy:新添加的任务,由新添加任务的线程负责执行
  3. ThreadPoolExecutor.DiscarOldestPolicy:丢弃任务队列中最老的元素
  4. ThreadPoolExecutor.DiscarPolicy:丢弃当前新加的任务

线程数目 

在使用线程池时,需要设置线程数目,那么设置多少合适?N?N+1?2N?都不是。

一个线程执行的代码主要有两大类:

  1. cpu密集型:代码的主要逻辑是在进行算术运算/逻辑判断
  2. IO密集型:代码主要进行的是IO操作

假设一个线程的所有代码都是cpu密集型,这时线程池的线程数量不应该超过N,设置比N更大时,也无法提高效率了(cpu吃满了)此时更多的线程反而增加了调度的开销.

再假设一个线程的代码都是IO密集型。这时程序不吃cpu,设置的线程数就可以超过N 

所以代码不同线程池的线程数目设置就不同,就无法知道一个代码具体内容是cpu聚集多一些,还是IO聚集多以一些。

正确的做法应该是:使用实验的方式,对程序进行性能测试,实验过程中尝试修改不同的线程池线程数目,看看那种情况下最符合要求

实现简单线程池

class MyThreadPool{//任务队列private BlockingQueue<Runnable> queue= new ArrayBlockingQueue<>(1);//通过这个方法,把任务添加到队列中public void submit(Runnable runnable) throws InterruptedException {//此处的拒绝策略是阻塞等待queue.put(runnable);}public MyThreadPool(int num){//创建出n个线程负责执行上述任务for (int i = 0; i < num; i++) {Thread t = new Thread(() -> {//让这个线程从队列中消费任务try {while(!Thread.interrupted()){Runnable runnable = queue.take();runnable.run();}} catch (InterruptedException e) {throw new RuntimeException(e);}});t.start();}}
}
public static void main(String[] args) throws InterruptedException {MyThreadPool myThreadPool = new MyThreadPool(4);for (int i = 0; i < 100; i++) {int id = i;Thread.sleep(500);myThreadPool.submit(new Runnable() {@Overridepublic void run() {//这里不能使用i因为匿名内部类的变量捕获,类里要使用不变值System.out.println("id" + id);}});}}

以上就是博主对线程池知识的分享,如果有不懂的或者有其他见解的欢迎在下方评论或者私信博主,也希望多多支持博主之后和博客!!🥰🥰

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

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

相关文章

智能控制,高效节能。ZLG致远电子能源智慧管理解决方案

面对楼宇及建筑群能源管理与设备控制的复杂需求&#xff0c;ZLG致远电子推出了一套能源智慧管理解决方案。该方案集设备管理、任务调度和数据可视化于一体&#xff0c;不仅实现数据的实时监控与分析&#xff0c;还助力系统节能降耗。 ZLG致远电子能源智慧管理解决方案 在ZLG致…

ShareSDK 企业微信

本篇文档主要讲解如何使用企业微信并进行分享和授权。 创建应用 登录企业微信并通过企业认证。选择应用管理 > 应用 >创建应用。编辑应用信息。配置授权登录信息。 以下为创建过程示例&#xff0c;图中信息仅为示例&#xff0c;创建时请按照真实信息填写&#xff0c;否…

如何查看ubuntu版本

在当前的技术环境中&#xff0c;了解操作系统的具体版本对于用户来说至关重要。这不仅能确保软件兼容性&#xff0c;还有助于进行系统管理和故障排查。对于使用Ubuntu系统的用户来说&#xff0c;有几种不同的方法可以查看当前系统的版本。下面将详细介绍如何查看您的Ubuntu系统…

Spring Boot(快速上手)

Spring Boot 零、环境配置 1. 创建项目 2. 热部署 添加依赖&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional> </dependency&…

polarctf靶场[WEB]cookie欺骗、upload、签到

[web]cookie欺骗 考点&#xff1a;cookie值 工具&#xff1a;Burp Suite抓包 根据题目提示&#xff0c;cookie欺骗&#xff0c;所以要在cookie值寻找关键 进入网页之后&#xff0c;说只有admin用户才能得到flag&#xff0c;而我们此时只属于普通访客 我们查看cookie值&…

「Python程序设计」基本数据类型:字符串

​在python的程序设计过程中&#xff0c;字符串是需要经常处理的变量类型。字符串在程序中的存储方式&#xff0c;类似于一维数组&#xff0c;每个字符占据数组中的一个单元格。 字符串可以存储字符类型的变量&#xff0c;即使是数字类型&#xff0c;也可以通过字符串来进行存…

vue3+vite配置环境变量实现开发、测试、生产的区分

文章目录 一、为什么需要区分 (dev)、测试 (test) 和生产 (prod) 环境二、vue3的项目如何通过配置方式区分不同的环境1、创建不同环境的.env文件2、在不同的.env文件中配置相应的环境变量1&#xff09;.env.develoment2&#xff09;.env.test3&#xff09;.env.production 3、在…

Git之git stash高级用法(五十)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列…

Tomcat 部署与优化

目录 tomcat 简介 tomcat 构件 1. Web 容器 2. Servlet 容器 3.Jsp容器 tomcat 核心组件 Connector Container Service 子容器 总结 tomcat 部署 tomcat请求过程 tomcat部署 tomcat 优化 tomcat配置文件参数优化 JVM优化 tomcat 简介 提到Tomcat 就想到 java&a…

RISC-V vector(1) --- vector的引入与register说明

Vector相较于SIMD的优势 这两种实现方案&#xff0c;都是为了实现数据级并行性&#xff08;存在大量的数据可供程序同时计算&#xff09;&#xff1b; SIMD&#xff08;Single Instruction Multiple Data&#xff09; SIMD是将数据宽度和操作类型&#xff0c;都放在了指令中&a…

http应用层协议

一、万维网 用来存放各种资源的网络。 1、如何在万维网中表示一个资源 ? url ——统一资源定位符&#xff1b; 形式&#xff1a; <协议>://<主机>:<端口>/<路径>&#xff1b; <主机>:<端口>/<路径> //表示了资源所在的…

这个TOP 100 AI应用榜单,包含了所有你需要的使用场景(一)

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;专注于分享AI全维度知识&#xff0c;包括但不限于AI科普&#xff0c;AI工…

【Unity3D小技巧】Unity3D中实现FPS数值显示功能实现

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址QQ群&#xff1a;398291828 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 很简单也很使用的小技巧&#xff0c;就是在Unity…

【系统分析师】-综合知识-计算机系统基础

1、流水线的吞吐率是指流水线在单位时间里所完成的任务数或输出的结果数。设某流水线有 5 段&#xff0c;有 1 段的时间为 2ns &#xff0c;另外 4 段的每段时间为 1ns&#xff0c;利用此流水线完成 100 个任务的吞吐率约为&#xff08;16&#xff09;个/s 。 2、矢量图像通过使…

[NeurIPS 2024] Self-Refine: Iterative Refinement with Self-Feedback

Contents TL;DRReferences TL;DR 通过让 LLM 生成 feedback 不断 refine 自身的回答&#xff0c;可以提升回答效果&#xff0c;但也会带来不可忽视的推理开销 References Madaan, Aman, et al. “Self-refine: Iterative refinement with self-feedback.” Advances in Neura…

广州网站制作seo优化技巧

随着互联网的迅速发展&#xff0c;越来越多的企业意识到网站对于品牌推广和销售的重要性。而在众多网站中&#xff0c;如何让自己的站点脱颖而出&#xff0c;是每个网站管理员和SEO从业者必须面对的挑战。特别是对于广州这样一个经济繁荣、竞争激烈的城市&#xff0c;网站制作和…

基于R语言进行AMMI分析2

接续上文【基于R语言进行AMMI分析1】 1、AMMI()函数的结果解读 # 加载agricolae包 library(agricolae) # 加载数据 data(plrv) # 查看数据 head(plrv) model<-with(plrv,AMMI(Locality,Genotype,Rep,Yield,PCTRUE)) # 查看方差分析结果 model$ANOVA # 查看主成分的方差分析…

minio 后端大文件分片上传,合并,删除分片

背景 网上大多数minio大文件上传都是采用后台返回前端预上传链接&#xff0c;然后由前端去put请求直接和minio通信上传分片文件&#xff0c;然后调用后台合并分片逻辑来达到快申诉上传的目的&#xff0c;详情可以参考我的上两篇文章 最近有个项目域名是https的&#xff0c;但…

详解华为项目管理,附华为高级项目管理内训材料

&#xff08;一&#xff09;华为在项目管理中通过有效的沟通、灵活的组织结构、坚持不懈的努力、细致的管理和科学的考核体系&#xff0c;实现了持续的创新和发展。通过引进先进的管理模式&#xff0c;强调以客户需求为导向&#xff0c;华为不仅优化了技术管理和项目研发流程&a…

单片机原理及技术(八)—— 串行口的工作原理及应用

目录 一、串行通信基础 1.1 并行通信与串行通信 1.1.1 并行通信 1.1.2 串行通信 1.2 同步通信与异步通信 1.3 串行通信的传输模式 二、串行口的结构 2.1 串行口控制寄存器SCON 2.1.1 SM0、SM1 2.1.2 SM2 2.1.3 REN 2.1.4 TB8 2.1.5 RB8 2.1.6 TI 2.1.7 RI 2.2 …