Java自带的线程池及调用、ThreadPoolExecutor类(线程池的7大参数)、任务队列及底层原理

day32

线程池

引入

一个线程完成一项任务所需时间为:

  1. 创建线程时间 - Time1
  2. 线程中执行任务的时间 - Time2
  3. 销毁线程时间 - Time3

注意:优化在Time1,Time3(创建销毁线程费时间)

为什么需要线程池

  1. 线程池技术正是关注如何缩短或调整Time1和Time3的时间,从而提高程序的性能。项目中可以把Time1,T3分别安排在项目的启动和结束的时间段或者一些空闲的时间段
  2. 线程池不仅调整Time1,Time3产生的时间段,而且它还显著减少了创建线程的数目,提高线程的复用率
  3. 系统启动一个新线程的成本是比较高的,因为涉及与操作系统的交互,在这种情形下,使用线程池可以很好地提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,优先考虑使用线程池

为什么使用线程池?

1.优化并调成创建线程和销毁线程的事件

2.提高线程的复用率

3.有效控制项目中的线程目数

Java提供的线程池

ExecutorService:线程池的接口

Executors:创建各种线程池的工具类

理解:实现了ExcutorService接口;底层:死循环,监视有没有提交任务,所以pool.shutdown关闭

分类

1.单个线程的线程池

2.指定线程的线程池

3.可缓存线程的线程池

理解:先没有线程,等有任务就添加一定数量的线程,去分批次执行,等有线程空闲了就干掉

缺点:线程目数不可控

4.延迟任务的线程池

多少时间运行一次(TimeUnit提供了很多时间单位)

public class Test {public static void main(String[] args) {//创建单个线程的线程池//ExecutorService pool = Executors.newSingleThreadExecutor();//创建指定线程的线程池//ExecutorService pool = Executors.newFixedThreadPool(3);//创建可缓存线程的线程池,自动回收60s闲置线程ExecutorService pool = Executors.newCachedThreadPool();//循环创建任务对象,并提交给线程池for (int i = 1; i <= 100; i++) {pool.execute(new Task(i));//提交任务,i为任务编号(方便理解)}pool.shutdown();//关闭线程池}
}
class Task implements Runnable{private int i;public Task(int i) {this.i = i;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "-->" + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}
}
public class Test01 {public static void main(String[] args) throws InterruptedException {//获取延迟任务的线程池ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor();//设置延迟时间(时间,时间单位)pool.awaitTermination(6, TimeUnit.SECONDS);//循环创建任务对象,并提交给线程池for (int i = 1; i <= 100; i++) {//创建任务对象Task task = new Task(i);//提交任务pool.execute(task);}//关闭线程池pool.shutdown();	}
}
public class Task implements Runnable{private int num;public Task(int num) {this.num = num;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "处理了第" + num + "个任务");}}

深入源码

ExecutorService pool = Executors.newSingleThreadExecutor();

ExecutorService pool = Executors.newFixedThreadPool(3);

ExecutorService pool = Executors.newCachedThreadPool();

三种线程池底层都是ThreadPoolExecutor类的对象

-- 分析ThreadPoolExecutor类的构造方法源码--------------------------------
public ThreadPoolExecutor(int corePoolSize,		------------- 核心线程数量 int maximumPoolSize,	------------- 最大线程数量 long keepAliveTime,		------------- 闲置时间,作用于核心线程数与最大线程数之间的线程TimeUnit unit,			------------- keepAliveTime的时间单位(可以是毫秒、秒....)BlockingQueue<Runnable> workQueue, -- 任务队列ThreadFactory threadFactory, -------- 线程工厂RejectedExecutionHandler handler ---- 达到了线程界限和队列容量时的处理方案(拒绝策略)
) {}
执行步骤:1.创建线程池后2.任务提交后,查看是否有核心线程:3.1 没有 -> 就创建核心线程 -> 执行任务 -> 执行完毕后又回到线程池中3.2-> 查看是否有闲置核心线程:4.1-> 执行任务 -> 执行完毕后又回到线程池4.2 没有 -> 查看当前核心线程数是否核心线程数量:5.1-> 就创建核心线程 -> 执行任务 -> 执行完毕后又回到线程池中5.2-> 查看任务列表是否装载满:6.1 没有 -> 就放入列表中,等待出现闲置线程6.2 装满 -> 查看是否有普通线程(核心线程数到最大线程数量之间的线程)7.1 没有 -> 就创建普通线程 -> 执行任务 -> 执行完毕后又回到线程池中7.2-> 查看是否有闲置普通线程7.1.1-> 执行任务 -> 执行完毕后又回到线程池中7.1.2 没有 -> 查看现在所有线程数量是否为最大线程数:8.1-> 执行处理方案(默认处理抛出异常)8.2->就创建普通线程-> 执行任务 -> 执行完毕后又回到线程池中     			
注:1.为了更好的理解,在这里区分核心线程和普通线程,实际上区分的这么清楚,都是线程2.默认的处理方案就是抛出RejectedExecutionException
总结:核心线程满载 -> 任务队列 -> 普通线程 -> 拒绝策略-- 分析单个线程的线程池的源码 --------------------------------
ExecutorService pool = Executors.newSingleThreadExecutor();
new ThreadPoolExecutor(1, -- 核心线程数量 1, -- 最大线程数量 0L, -- 闲置时间TimeUnit.MILLISECONDS, -- 时间单位(毫秒)new LinkedBlockingQueue<Runnable>() -- 无界任务队列,可以无限添加任务
)  
-- 分析指定线程的线程池的源码 --------------------------------
ExecutorService pool = Executors.newFixedThreadPool(3);
new ThreadPoolExecutor(nThreads, -- 核心线程数量 nThreads, -- 最大线程数量 0L, -- 闲置时间TimeUnit.MILLISECONDS, -- 时间单位(毫秒)new LinkedBlockingQueue<Runnable>()-- 无界任务队列,可以无限添加任务
)
-- 创建可缓存线程的线程池 -----------------------------------
new ThreadPoolExecutor(0, -- 核心线程数量 Integer.MAX_VALUE,-- 最大线程数量 60L, -- 闲置时间TimeUnit.SECONDS, -- 时间单位()new SynchronousQueue<Runnable>() -- 直接提交队列(同步队列):没有容量队列

任务队列详解

队列名称详解
LinkedBlockingQueue无界任务队列使用无界任务队列,线程池的任务队列可以无限制的添加新的任务,而线程池创建的最大线程数量就是你corePoolSize设置的数量,也就是说在这种情况下maximumPoolSize这个参数是无效的,哪怕你的任务队列中缓存了很多未执行的任务,当线程池的线程数达到corePoolSize后,就不会再增加了;若后续有新的任务加入,则直接进入队列等待,当使用这种任务队列模式时,一定要注意你任务提交与处理之间的协调与控制,不然会出现队列中的任务由于无法及时处理导致一直增长,直到最后资源耗尽的问题
SynchronousQueue 同步任务队列 直接提交任务队列使用直接提交任务队列,队列没有容量,每执行一个插入操作就会阻塞,需要再执行一个删除操作才会被唤醒,反之每一个删除操作也都要等待对应的插入操作。 任务队列为SynchronousQueue,创建的线程数大于maximumPoolSize时,直接执行了拒绝策略抛出异常。 使用SynchronousQueue队列,提交的任务不会被保存,总是会马上提交执行。如果用于执行任务的线程数量小于maximumPoolSize,则尝试创建新的线程,如果达到maximumPoolSize设置的最大值,则根据你设置的handler执行拒绝策略。因此这种方式你提交的任务不会被缓存起来,而是会被马上执行,在这种情况下,你需要对你程序的并发量有个准确的评估,才能设置合适的maximumPoolSize数量,否则很容易就会执行拒绝策略;
ArrayBlockingQueue有界任务队列使用有界任务队列,若有新的任务需要执行时,线程池会创建新的线程,直到创建的线程数量达到corePoolSize时,则会将新的任务加入到等待队列中。若等待队列已满,即超过ArrayBlockingQueue初始化的容量,则继续创建线程,直到线程数量达到maximumPoolSize设置的最大线程数量,若大于maximumPoolSize,则执行拒绝策略。在这种情况下,线程数量的上限与有界任务队列的状态有直接关系,如果有界队列初始容量较大或者没有达到超负荷的状态,线程数将一直维持在corePoolSize以下,反之当任务队列已满时,则会以maximumPoolSize为最大线程数上限。
PriorityBlockingQueue优先任务队列使用优先任务队列,它其实是一个特殊的无界队列,它其中无论添加了多少个任务,线程池创建的线程数也不会超过corePoolSize的数量,只不过其他队列一般是按照先进先出的规则处理任务,而PriorityBlockingQueue队列可以自定义规则根据任务的优先级顺序先后执行。

对优先队列的使用说明:

public class Test {public static void main(String[] args) {ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS, new PriorityBlockingQueue<>(), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());for (int i = 1; i <= 10; i++) {pool.execute(new Task(i));}pool.shutdown();}
}
class Task implements Runnable,Comparable<Task>{private int priority;public Task(int priority) {this.priority = priority;}@Overridepublic void run() {try {Thread.sleep(1000);System.out.println(Thread.currentThread().getName() + " priority:" + this.priority);} catch (InterruptedException e) {e.printStackTrace();}}//当前对象和其他对象做比较,当前优先级大就返回-1,优先级小就返回1,值越小优先级越高@Overridepublic int compareTo(Task o) {return (this.priority>o.priority)?-1:1;}
}
总结:除了第一个任务直接创建线程执行外,其他的任务都被放入了优先任务队列,按优先级进行了重新排列执行,且线程池的线程数一直为corePoolSize,也就是只有一个。
代码实现
无界队列

理解:这个队列没有上限

继承关系:LinkedBlockingQueue -> AbstractQueue -> AbstractCollection

小结:

1.LinkedBlockingQueue是Collection集合家族的一员

2.Collection集合家族(List、Set、Queue)

3.LinkedBlockingQueue数据结构单向链表

缺点:LinkedBlockingQueue可能造成内存溢出

有界队列

继承关系:ArrayBlockingQueue -> AbstractQueue -> AbstractCollection

小结:

1.ArrayBlockingQueue是Collection集合家族的一员

2.Collection集合家族(List、Set、Queue)

3.ArrayBlockingQueue数据结构一维数组

public class Test01 {public static void main(String[] args) throws InterruptedException {//创建无界队列LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>();//创建有界队列//ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(6);//添加元素queue.put("aaa");queue.put("bbb");queue.put("ccc");queue.put("ddd");queue.put("eee");queue.put("fff");//删除元素queue.remove("ccc");//遍历队列Iterator<String> it = queue.iterator();while (it.hasNext()) {String string = it.next();System.out.println(string);}	}
}
优先队列

继承关系:ArrayBlockingQueue -> AbstractQueue -> AbstractCollection

小结:

1.ArrayBlockingQueue是Collection集合家族的一员

2.Collection集合家族(List、Set、Queue)

3.ArrayBlockingQueue数据结构一维数组

public class Test03 {public static void main(String[] args) throws InterruptedException {//创建优先队列PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();//无参构造底层使用的是元素的内置比较器//添加元素queue.add(new Task(3));queue.add(new Task(1));queue.add(new Task(4));queue.add(new Task(2));//遍历取出队列while(!queue.isEmpty()){//删除第一元素Task poll = queue.poll();System.out.println(poll.getPriority());}}
}

Task类

public class Task implements Runnable,Comparable<Task>{private int priority;//优先级别public Task(int priority) {this.priority = priority;}@Overridepublic void run() {System.out.println("任务被处理了");}//当前对象和其他对象做比较,当前优先级大就返回-1,优先级小就返回1,值越小优先级越高@Overridepublic int compareTo(Task o) {return Integer.compare(this.priority, o.priority);}public int getPriority() {return priority;}
}

总结

1.Java自带的线程池
单个线程的线程池
指定线程个数的线程池
可缓存的线程池
延迟任务的线程池

2.线程池的7大参数
核心线程数
最大线程数
任务队列(有界、无界、同步、优先队列)
拒绝策略
闲置时间
时间单位
线程工厂

3.线程池的调用步骤(核心线程、任务队列、普通线程、拒绝策略)

4.任务队列及底层原理(有界、无界、同步、优先队列)

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

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

相关文章

Facebook轮播广告是什么?投放过程中有哪些需要注意的吗?

轮播广告是Facebook广告形式中的一种&#xff0c;可以把3—5个广告合并到一个可滚动的广告单元中。轮播广告会出现在新鲜事即News Feed中&#xff0c;是独立站卖家常用的一种广告形式 为什么选择轮播广告&#xff1f; 转化率更高&#xff1a;相较于单图广告&#xff0c;轮播广…

基于单片机输出PWM 波控制的音乐喷泉系统设计

摘 要:随着经济、文化的发展,音乐喷泉作为一种观赏性较高的艺术水景,逐渐出现在全国城市中。但在音乐与喷泉喷射配合过程中,并没有实现二者同步,存在时滞。为此,设计了以STC89C52单片机为核心的小型音乐喷泉控制系统。音乐信号经放大通过A/D转换器处理,送入…

Springboot+vue的高校科研信息管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的高校科研信息管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#x…

计算机网络(第八版)-第1章课后习题参考答案

计算机网络(第八版)-第1章课后习题参考答案 本文是对自己之前文章的格式化&#xff1a;https://blog.csdn.net/qq_46396470/article/details/132788972?spm1001.2014.3001.5502 T1-01 计算机网络向用户可以提供哪些服务&#xff1f; 连通性和共享 &#xff0c;例如音频&…

修改docker容器日志大小

docker-compose.yaml logging:options:max-size: "10m"docker run docker run -d \--name example-container \--log-opt max-size10m \--log-opt max-file3 \nginx:latestdocker daemon全局配置 /etc/docker/daemon.json 参考文档&#xff1a;https://docs.docker…

【Node.JS】koa

文章目录 概述koa和express对比koa下载安装使用1.创建koa项目文件目录2. 创建koa服务3. 添加路由 koa-router4. 数据库服务 mongodb5. 添加请求参数json处理 koa-bodyparser6. 用户接口举例7.引入koa一些常用插件8.用户登录验证 koa-jwt9.webpack生产打包 来源 概述 Koa 是一个…

A fatal error occurred: MD5 of file does not match data in flash!问题解决

采用的芯片是ESP32-S3-WROOM&#xff0c;16MB FLASH 开发环境是Arduino&#xff0c;烧录到100%后直接报错。 以为是Arduino的问题&#xff0c;用esp-idf开发的程序&#xff0c; 烧录的过程中&#xff0c;也是直接报错如下&#xff1a; esptool.py v4.7.0 Serial port /dev/…

k8s下搭建redis集群

记录一下近期实现的在k8s上搭建redis集群的过程 1、新建存储类 主要是为了和其它服务的存储类区分一下 redis-beta-storage 2、编写configMap redis启动时从configMap中读取配置 bind&#xff1a;默认的127.0.0.1可能会导致其它ip地址无法远程访问&#xff0c;因此修改为0.0…

战略定位--选择什么赛道。成为什么样的企业。

2、原则&#xff1a;选择细分赛道&#xff0c;做头部客户 &#xff08;1&#xff09;刘伯承&#xff1a;打仗&#xff1a;五行要定、五行不定输的干净、定阵地、了解我情、了解敌情、目标清楚、时间清楚 &#xff08;2&#xff09;中小企业不在规模在 &#xff08;3&#xff09…

揭秘情绪识别:如何让AI读懂你的心声?

最近我在研究大语言模型&#xff0c;想用它来给样本打分。 起初&#xff0c;我尝试让模型用1到5分来评分&#xff0c;但它总是极端地给出最低分或最高分&#xff0c;评分缺乏中间地带。 于是我换了个方法&#xff0c;不再用数字&#xff0c;而是用描述性的词语&#xff0c;比…

【Golang星辰图】加速开发的利器:揭秘Go语言的高效工具集

工具库&#xff1a;提升Go语言开发效率的利器 前言 在现代软件开发中&#xff0c;拥有高效、灵活的工具库是开发人员提高工作效率和简化开发流程的关键。本文将介绍几个优秀的工具库&#xff0c;它们为Go语言开发提供了强大的功能和便捷的工具&#xff0c;帮助开发人员更轻松…

农村集中式生活污水分质处理及循环利用技术指南

立项单位&#xff1a;生态环境部土壤与农业农村生态环境监管技术中心、山东文远环保科技股份有限公司、北京易境创联环保有限公司、中国环境科学研究院、广东省环境科学研究院、中铁第五勘察设计院集团有限公司、中华环保联合会水环境治理专业委员会 本文件规定了集中式村镇生活…

常用的几个ChatGPT网站,国内可用!

&#x1f310; 链接&#xff1a; ChatAI &#x1f3f7;️ 标签&#xff1a; GPT-4 免费体验 付费选项 &#x1f4dd; 简介&#xff1a;ChatAI 是一个功能强大的平台&#xff0c;提供GPT-4&#xff0c;不限使用次数&#xff0c;不限Token。用户可以选择包月或是更灵活的按次付…

Discourse 最多允许有几个分类级别

和 DISCUZ 不同&#xff0c;DISCUZ 可以允许分类下面还有分类&#xff0c;再继续分类这种嵌套式分类。 Discourse 最多只允许有 2 个分类。 如果你在已有的分类下再继续分类的话&#xff0c;系统会提示错误&#xff1a; 意思就是子分类不能再分子分类。 Discourse 尽量采取了…

数据结构(六)——图

六、图 6.1 图的基本概念 图的定义 图&#xff1a;图G由顶点集V和边集E组成&#xff0c;记为G (V, E)&#xff0c;其中V(G)表示图G中顶点的有限非空集&#xff1b;E(G) 表示图G中顶点之间的关系&#xff08;边&#xff09;集合。若V {v1, v2, … , vn}&#xff0c;则用|V|…

计算机基础系列 —— 虚拟机代码翻译器(1)

“Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program.” ―Linus Torvalds 文中提到的所有实现都可以参考&#xff1a;nand2tetris_sol&#xff0c;但是最好还是自己学习课程实现一…

Linux基础 超详细!!!

一、Linux基础命令 &#xff08;一&#xff09;Linux的目录结构 1、盘符 Linux中无盘符的概念&#xff0c;只有一个根目录 “/” 也就是只有一棵树) 而windows里面有多棵树&#xff0c;每个盘符都是一棵树的根 在Linux系统中&#xff0c;路径之间的层级关系&#xff0c;使用…

Flutter 使用 AndroidStudio 给(Android 安卓)进行签名方法

一、使用 AndroidStudio 创建签名 使用 AndroidStudio 打开 Flutter项目中的 android 文件夹首次打开 AndroidStudio 会加载一会。菜单栏 &#xff1a; Build -> Generate Signed Bundle APK... 选中 APK -> Next点击Create new....下面按照需求填写即可- 文件夹选择 项…

容器二(List接口)

目录 List 特点和常用方法 List 的常用方法&#xff1a; 两个 List 之间的元素处理 List 中操作索引的常用方法 List是指“有顺序、可重复”的容器。 List接口是Collection接口的子接口&#xff0c;因此Collection 接口中的方法 List 接口都拥有&#xff1b;同时&#xff0…

Oracal执行计划解析

概述 | Id | Operation | Name | Rows | Bytes | TempSpc | Cost (%CPU) | Time | ----------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1…