并发编程---线程池(六)
创始人
2025-05-28 02:09:27
0

阻塞队列的应⽤——线程池

  • 一 线程池基本概念
  • 二 线程池三种常⽤创建⽅式
    • 2.1.newFixedThreadPool线程池:
    • 2.2.newSingleThreadExecutor线程池:
    • 2.3.newCachedThreadPool线程池:
    • 2.4. 线程池代码演示
  • 三 线程池创建的七个参数
  • 四 线程池底层原理
    • 理解:
    • 案例图:
    • 原理图:
    • 流程图:
    • 线程池使用注意:
  • 五 线程池的拒绝策略
    • AbortPolicy拒绝策略
    • CallerRunsPolicy拒绝策略
    • DiscardOldestPolicy拒绝策略
    • DiscardPolicy拒绝策略
  • 六 实际⽣产使⽤哪⼀个线程池?
  • 七 ⾃定义线程池参数选择

一 线程池基本概念

概念: 线程池主要是控制运⾏线程的数量,将待处理任务放到等待队列,然后创建线程执⾏这些任务。 如果超过了最⼤线程数,则等待。
为什么⽤线程池?

10年前单核CPU电脑,假的多线程,像⻢戏团⼩丑玩多个球,CPU需要来回切换。
现在是多核电脑,多个线程各⾃跑在独⽴的CPU上,不⽤切换效率⾼。

线程池的优点:
线程池做的⼯作主要是控制运⾏的线程数量,处理过程中将任务放⼊队列,然后在线程创建后启动这些任务,如果线程数量超过了最⼤数量,超出数量的线程排队等候,等其他线程执⾏完毕,再从队列中取出任务来执⾏。
线程池的主要特点为:线程复⽤;控制最⼤并发数;管理线程。

  1. 线程复⽤:不⽤⼀直new新线程,重复利⽤已经创建的线程来降低线程的创建和销毁开销,节省系统资源。
  2. 提⾼响应速度:当任务达到时,不⽤创建新的线程,直接利⽤线程池的线程。
  3. 管理线程:可以控制最⼤并发数,控制线程的创建等。

体系: Executor→→ExecutorService→AbstractExecutorService→ThreadPoolExecutor。
ThreadPoolExecutor是线程池创建的核⼼类。类似Arrays、Collections工具类,Executor也有自己的工具类Executors。
在这里插入图片描述

在这里插入图片描述

二 线程池三种常⽤创建⽅式

2.1.newFixedThreadPool线程池:

使⽤ LinkedBlockingQueue实现,定⻓线程池。
特点:执⾏⻓期任务性能好,创建⼀个线程池,⼀池有N个固定的线程,有固定线程数的线程
在这里插入图片描述

2.2.newSingleThreadExecutor线程池:

使⽤ LinkedBlockingQueue实现,⼀池只有⼀个线程。
特点:⼀个任务⼀个任务的执⾏,⼀池⼀线程
在这里插入图片描述

2.3.newCachedThreadPool线程池:

使⽤ SynchronousQueue实现,变⻓线程池。
特点:执⾏很多短期异步任务,线程池根据需要创建新线程,但在先前构建的线程可⽤时将重⽤他们。 可扩容,遇强则强
在这里插入图片描述

2.4. 线程池代码演示

任务类
模拟十个客户来办理业务

 private static void threadPoolTask(ExecutorService threadPool) {//模拟有10个顾客来办理业务try {for (int i = 1; i <= 10; i++) {threadPool.execute(() -> {System.out.println(Thread.currentThread().getName() + "\t办理业务");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}});}} catch (Exception e) {e.printStackTrace();} finally {threadPool.shutdown();}}
  1. newFixedThreadPool线程池
    创建线程数为5,观察结果发现5个线程一起执行
 //一个池子有5个工作线程,类似银行有5个受理窗口threadPoolTask(Executors.newFixedThreadPool(5));

在这里插入图片描述

  1. newSingleThreadExecutor线程池

创建newSingleThreadExecutor线程池,观察结果发现只有一个线程可以使用。

System.out.println("======Single Thread Pool=========");// //一个池子有1个工作线程,类似银行有1个受理窗口threadPoolTask( Executors.newSingleThreadExecutor() );

在这里插入图片描述

  1. newCachedThreadPool线程池:
    创建newCachedThreadPool,观察结果有10个客户,就有<=10个线程执行任务。有可能业务员办事快,接着又给其他客户办理业务
        System.out.println("=====Cached Thread Pool=======");// //不定量线程,一个池子有N个工作线程,类似银行有N个受理窗口threadPoolTask( Executors.newCachedThreadPool() );

在这里插入图片描述
问题:
上述我们使用10个客户来模拟,如果用100个呢,我们来观察结果
在这里插入图片描述

三 线程池创建的七个参数

在这里插入图片描述

参数意义
corePoolSize线程池中的常驻核⼼线程数
maximumPoolSize线程池中能够容纳同时并发的最⼤线程数,此值必须⼤于等于1
keepAliveTime多余的空闲线程的存活时间,当前池中线程数量超过corePoolSize时,当空闲时间达到keepAliveTime时,多余线程会被销毁直到只剩下corePoolSize个线程为⽌
unitkeepAliveTime存活时间的单位
workQueue任务队列,存放已提交但尚未执⾏的任务
threadFactory表示⽣成线程池中⼯作线程的线程⼯⼚,⽤于创建线程,⼀般默认的即可
handler拒绝策略,表示当队列满了,并且⼯作线程⼤于等于线程池的最⼤线程数(maximumPoolSize)时,如何来拒绝请求执⾏的runnable的策略

四 线程池底层原理

理解:

线程池的创建参数,就像⼀个银⾏。

  1. corePoolSize就像银⾏的“当值窗⼝“,⽐如今天有2位柜员在受理客户请求(任务)。
  2. 如果超过2个客户,那么新的客户就会在等候区(等待队列workQueue)等待。
  3. 当等候区也满了,这个时候就要开启“加班窗⼝”,让其它3位柜员来加班,此时达到最⼤窗⼝maximumPoolSize,为5个。
  4. 如果开启了所有窗⼝,等候区依然满员,此时就应该启动”拒绝策略handler ,告诉不断涌⼊的客户, 叫他们不要进⼊,已经爆满了。
  5. 由于不再涌⼊新客户,办完事的客户增多,窗⼝开始空闲,这个时候就通过 keepAlivetTime将多余的3个”加班窗⼝“取消,恢复到2个”当值窗⼝“

案例图:

在这里插入图片描述

原理图:

上⾯银⾏的例⼦,实际上就是线程池的⼯作原理。
在这里插入图片描述

流程图:

在这里插入图片描述
流程:

  1. 在创建了线程池后,开始等待请求。
  2. 当调⽤execute()⽅法添加⼀个请求任务时,线程池会做出如下判断:
    2.1 如果正在运⾏的线程数量⼩于corePoolSize,那么⻢上创建核⼼线程运⾏执⾏这个任务;
    2.2 如果正在运⾏的线程数量⼤于或等于corePoolSize,那么将这个任务放⼊队列;
    2.3 如果这个时候等待队列已满,且正在运⾏的线程数量⼩于maximumPoolSize ,那么还是要创
    建⾮核⼼线程⽴刻运⾏这个任务;
    2.4 如果这个时候等待队列已满,且正在运⾏的线程数量⼤于或等于 maximumPoolSize,那么线程池会启动饱和拒绝策略来执⾏。
  3. 当⼀个线程完成任务时,它会从等待队列中取出下⼀个任务来执⾏。
  4. 当⼀个线程⽆事可做超过⼀定的时间( keepAliveTime)后,线程会判断:
    如果当前运⾏的线程数⼤于 corePoolSize,那么这个⾮核⼼线程就被停掉。当线程池的所有任
    务完成后,它最终会收缩到corePoolSize的⼤⼩。

线程池使用注意:

《Java 开发⼿册》是阿⾥巴巴集团技术团队:

在这里插入图片描述

五 线程池的拒绝策略

在这里插入图片描述
当等待队列满时,且达到最⼤线程数,再有新任务到来,就需要启动拒绝策略。JDK提供了四种拒绝策
略,分别是:

  1. AbortPolicy:默认的策略,直接抛出 RejectedExecutionException异常,阻⽌系统正常运⾏。
  2. CallerRunsPolicy:既不会抛出异常,也不会终⽌任务,⽽是将任务返回给调⽤者,从⽽降低新任务的流量。
  3. DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加⼊队列中尝试再次提交任务。
  4. DiscardPolicy:该策略默默地丢弃⽆法处理的任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的⼀种策略。

AbortPolicy拒绝策略

在这里插入图片描述

CallerRunsPolicy拒绝策略

在这里插入图片描述

DiscardOldestPolicy拒绝策略

在这里插入图片描述

DiscardPolicy拒绝策略

在这里插入图片描述

六 实际⽣产使⽤哪⼀个线程池?

单⼀、可变、定⻓都不⽤!原因就是FixedThreadPoolSingleThreadExecutor底层都是⽤ LinkedBlockingQueue实现的,这个队列最⼤⻓度为 Integer.MAX_VALUE,显然会导致OOM。所以实际⽣产⼀般⾃⼰通过的7个参数,⾃定义线程池

  System.out.println("=====Custom Thread Pool=======");threadPoolTask( new ThreadPoolExecutor(2,5,1L,TimeUnit.SECONDS,new LinkedBlockingQueue<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardPolicy()));

结果分析在这里插入图片描述
在这里插入图片描述

七 ⾃定义线程池参数选择

对于CPU密集型任务,最⼤线程数是CPU线程数+1。
对于IO密集型任务(文件上传下载),尽量多配点,可以是CPU线程数*2,或者CPU线程数/(1-阻塞系数)。
在这里插入图片描述

IO密集型,即该任务需要⼤量的IO,即⼤量的阻塞。
在单线程上运⾏IO密集型的任务会导致浪费⼤量的CPU运算能⼒浪费在等待。
所以在IO密集型任务中使⽤多线程可以⼤⼤的加速程序运⾏,及时在单核CPU上,这种加速主要就是利⽤了被浪费掉的阻塞时间。
IO密集型时,⼤部分线程都阻塞,故需要多配置线程数:
**参考公式:**CPU核数 / 1 - 阻塞系数 阻塞系数在 0.8~0.9 之间
⽐如 8 核 CPU:8/1 - 0.9 = 80个线程数

相关内容

热门资讯

深信服校园招聘安全攻防A卷 牛客网的题目:牛客网公司真题_免费模拟题库_企业面试|笔试真题 谈谈在WEB类安全问题...
一篇文章带你了解TCP/IP模... 目录 1、OSI模型 2、具体模型图 3、以太网协议 4、ARP地址解析协议 5、IP地址分类
爱心代码李峋同款爱心 pyth... 目录 前言 一、python 1.python 第一个 2.python第二个 二、HTML 1....
python 虚拟环境的原理分... 摘要说明:本文对python虚拟环境的原理,进行了一些简单分析ÿ...
基于gin+Grom的Gowe... 目录一、前端Vue框架的应用1.1 下载前端写好的代码(vue)二、后端...
3.15~3.16学习总结 https://vjudge.net/contest/547627#problem/F求解联通块个数...
Qt之QUrl和QUrlQue... QUrlQUrl 类提供了一个方便的接口使用 URLs。最常见的使用QUrl 的方式是通过构造函数来...
微信小程序 |基于Flask框... 文章目录一、效果预览1.1 首页推荐图1.2 菜谱智能识别页面1.3 菜谱类别列表1.4 步骤详情二...
Java Virtual Ma... Java Virtual Machine(JVM,Java虚拟机...
都说软件测试岗位是个人都会,但... 上一个说软件测试简单的,已经被面试官问emo了... 现在已经过了 ”不会但我会学“ ...
数据库面试题——锁 了解数据库的锁吗? 锁是数据库系统区别于文件系统的一个关键特性,锁机制用...
k8s pod 基本信息 1,最小部署单元 2,包含多个容器(一组容器的...
*9 set up 注意点 1、set up 执行的时机:beforeCreate 之前执行一次,t...
Maven的生命周期与插件 一、maven对项目构建的生命周期划分为运行三个阶段。 1、clean:清理工作。 ...
系统集成项目管理工程师:第10... 第10章项目质量管理 一、目录 10.1 项目质量管理概论 10.1.1 质量及质量管理概念 10...
数据结构--二叉树 目录1.树概念及结构1.1数的概念1.2数的表示2.二叉树概念及结构2.1二叉树的概念2.2数据结构...
vue项目关于iframe嵌套... 1.需求 这两天工作中遇到一个这样的需求,切换tab标签时,要求对应的t...
【springboot】单元测... 1、JUnit5 的变化 Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元...
JVM学习笔记 01 - JV... JVM、JDK和JRE JVM全称 Java Virtual Machine,也就是我...
[ 红队知识库 ] Windo... 🍬 博主介绍 👨‍🎓 博主介绍:大家好...