【JUC】九、线程池ThreadPool

文章目录

  • 1、线程池
  • 2、分类
  • 3、线程池的使用
  • 4、工作流程
  • 5、拒绝策略
  • 6、线程池的七个参数
  • 7、自定义线程池
  • 8、什么时候考虑使用线程池?

1、线程池

线程池和数据库连接池的理念很相似,对于数据库连接池:普通的连接数据库是建立一个JDBC连接,执行完sql之后,就会关闭,即销毁connection对象,再次连接还需要重复上述步骤。当与数据库交互频繁时,这种模式会严重影响程序的性能,因此有了数据库连接池。对应到线程池thread pool,就是线程池里维护着多个线程,等待监督管理者分配执行任务。线程池带来的好处就是:

  • 降低资源消耗:降低避免频繁创建和销毁线程的代价
  • 提高响应速度:任务达到时,不用再等待创建线程
  • 线程管理方便:线程过多,调度开销大,用线程池可防止过分调度,且可以做统一的监控、分配、调优

关于线程切换的例子:10 年前单核 CPU 电脑,假的多线程,像马戏团小丑玩多个球,CPU 需要来回切换。 现在是多核电脑,多个线程各自跑在独立的 CPU 上,不用频繁切换,效率高。

2、分类

Java 中的线程池是通过 Executor 框架实现的,该框架中用到了 Executors(工具类)、ExecutorsExecutorService、ThreadPoolExecutor这几个类
在这里插入图片描述

线程池有以下几类:

  • 一池N线程:Executors.newFixedThreadPool(int num)
  • 一池一线程:Executors.newSingleThreadExecutor()
  • 可扩容池,根据需求创建一定数量的线程,遇强则强:Executors.newCachedThreadPool()

3、线程池的使用

  • 创建线程池对象
  • 调用execute方法提交任务
public class ThreadPoolDemo {public static void main(String[] args) {//一池五线程ExecutorService threadPool = Executors.newFixedThreadPool(3);//一池1线程ExecutorService threadPool1 = Executors.newSingleThreadExecutor();//一池可扩容线程ExecutorService threadPool2 = Executors.newCachedThreadPool();//提交10次任务到线程池try{for (int i = 1; i <= 20; i++) {//提交任务到另一线程(线程池中的)threadPool2.execute(() -> {System.out.println(Thread.currentThread().getName() + "线程正在办理业务");});}}catch(Exception e){e.printStackTrace();}finally{threadPool2.shutdown();}}
}

以可扩容线程池为例:

在这里插入图片描述

4、工作流程

在这里插入图片描述

如图,此时常驻线程数为2,最大线程数为5,阻塞队列长度为3(黑点),此时来了两个任务1和2 ⇒ 直接常驻线程 ⇒ 那两个任务还执行完,又来了几个任务3、4、5 ⇒ 这时不是直接上最大线程,而是进入阻塞队列 ⇒ 此时又来了三个人6、7、8 ⇒ 发现阻塞队列也满了,那就开启最大线程处理6、7、8的业务 (注意新开的线程不是去处理阻塞队列了,阻塞队列的3、4、5还是在队列中继续等待) ⇒ 此时又来了一个任务9 ⇒ 走拒绝策略

注意这几点:

ExecutorService pool = Executors.newSingleThreadExecutor();
  • 执行上面这句,并不会创建线程,而是执行pool.execute方法提交任务时才创建
  • 常驻线程用完了,再来任务,不是直接按最大线程数启动新线程,而是阻塞队列
  • 阻塞队列满了以后,按最大线程数启动新线程,且新线程处理的不是阻塞队列里的任务

看下源码,从提交任务的execute方法打断点,进入execute方法:

在这里插入图片描述

在这里插入图片描述

5、拒绝策略

阻塞队列和最大线程数量都用完后,走拒绝策略,JDK内置的拒绝策略有:

  • AbortPoligy(默认):直接抛出RejectedExecutionExption异常阻止系统正常运行
  • CallerRunsPoliy:既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,谁让你来的,你找谁去
  • DiscardOldestPoliy:抛弃阻塞队列中等待最久的任务,然后把当前任务加入队列中
  • DiscardPolicy:默默地丢弃无法处理的任务,不予任何处理也不抛出异常,如果允许任务丢失,这是最好的一种策略

6、线程池的七个参数

查看源码可以发现,不管是三种线程池中的哪种,最后都是return new ThreadPoolExecutor,关于ThreadPoolExecutor类:

在这里插入图片描述

  • int corePoolSize:常驻线程数量
  • int maximumPoolSize:最大线程数量
  • long keepAliveTime:线程存活时间,线程多长时间没被使用就关闭
  • TimeUnit unit:存活时间的单位
  • BlockingQueue workQueue:常驻线程用完了,再来请求线程,进入阻塞队列
  • ThreadFactory threadFactory:线程工厂
  • RejectedExecutionHandler handler:拒绝策略

以银行为例对比:银行大厅一共有10个窗口(最大线程数量),但平时一般只开5个(常驻线程数量),某天办理业务的人很多,5个窗口不够用,其余人来了就先在大厅椅子上坐着等(阻塞队列),结果椅子坐满了,还有人陆续来,于是10个窗口全开,还来很多人,那就只能告诉新来的今天轮不到你办了(拒绝策略)。

7、自定义线程池

Executors工具类可以创建三种线程池,但通常自定义线程池是因为,Executors返回的线程池对象有以下两个问题:

  • 对于FixedThreadPool和SingleThreadPool,代码底层用的阻塞队列是LinkedBlockingQueue类型的,队列长度为Integer.MAX_VALUE,可能堆积大量请求,导致OOM
  • 对于CachedThreadPool,其源码中写的最大线程数量为Integer.MAX_VALUE,创建大量线程,调度难度大且会OOM
public class ThreadPoolDemo2 {public static void main(String[] args) {ExecutorService threadPool = new ThreadPoolExecutor(2,    //常驻或核心线程数5,    //最大线程数2L,TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),  //阻塞队列Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()  //拒绝策略);try {for (int i = 1; i <= 20; i++) {threadPool.execute(() -> {System.out.println(Thread.currentThread().getName() + "线程正在办理业务");});}} catch (Exception e) {e.printStackTrace();} finally {threadPool.shutdown();}}
}

在这里插入图片描述

8、什么时候考虑使用线程池?

到这儿,线程池的作用、分类、底层代码逻辑、参数与策略的问题基本清晰,那什么时候考虑去使用线程池呢?==> 线程池适合处理耗时任务,可以充分使用目前服务器的硬件资源,加快处理速度。更确切的说是:

  • 单个任务处理时间比较短
  • 但需要处理的任务的数量大

此时,如果不使用线程池,随意启动许多线程,容易导致系统因创建大量线程而OOM且过渡调度(过渡切换)。比如工作中遇到一个excel数据转换后批量写入库里,就可拆为一批批的小任务去insert。还有帖子说需要限制并发执行的任务数量时也可以用线程池,这儿我先想到的反而是Semaphore信号灯这个JUC辅助类。

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

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

相关文章

Linux_系统信息_uname查看内核版本、内核建立时间、处理器类型、顺便得到操作系统位数等

1、uname --help 使用uname --help查看uname命令的帮助信息 2、uname -a 通过上面的help就知道-a选项显示全部内容时的含义了。 内核名是Linux主机名是lubancat&#xff0c;如果想看主机名可以使用命令hostname&#xff1b;内核版本是Linux 4.19.232&#xff0c;建立时间为2…

C语言之深入指针及qsort函数(五)(详解介绍)

C语言之深入指针 在这篇博客看不懂的可以看看这篇C语言之深入指针&#xff08;四&#xff09;在上篇博客中介绍了&#xff1a; 函数指针变量函数指针数组简易计算器的实现\ 文章目录 C语言之深入指针1 回调函数2 qsort函数的使用2.1 使用冒泡排序排序整型数组2.2 使用qsort函数…

什么是脏读、不可重复读、幻读讲解

数据库隔离级别是数据库管理系统中一个重要的概念&#xff0c;它定义了事务之间的可见性和影响。在多用户并发访问数据库时&#xff0c;隔离级别能够确保事务之间的相互独立性&#xff0c;避免数据不一致的问题。本文将深入探讨三种常见的并发问题&#xff1a;脏读、不可重复读…

QTableWidget 设置列宽行高大小的几种方式及其他常用属性设置

目录 效果&#xff1a; 1.列宽、行高自动分配 2.固定值 3.随内容分配列宽 随内容分配行高 4.水平方向标签拓展剩下的窗口部分&#xff0c;填满表格 5.列宽是自动分配的&#xff0c;但是第一列可手动调整宽度&#xff0c;而表格整体的列宽仍是自动分配的。第二、三列办法调…

String字符串性能优化的几种方案

原创/朱季谦 String字符串是系统里最常用的类型之一&#xff0c;在系统中占据了很大的内存&#xff0c;因此&#xff0c;高效地使用字符串&#xff0c;对系统的性能有较好的提升。 针对字符串的优化&#xff0c;我在工作与学习过程总结了以下三种方案作分享&#xff1a; 一.优…

SpringBoot整合Redis使用基于注解的缓存

环境准备 注解 EnableCaching CacheConfig CacheConfig 提供了一种在类级别共享公共缓存相关设置的机制。 | 参数 | 作用 | | | — | — | — | | cacheNames | 使用在类上的默认缓存名称 | | | keyGenerator | 用于类的默认KeyGenerator的bean名称 | | | cacheManager | 自定…

【ARM Trace32(劳特巴赫) 使用介绍 2.1 -- TRACE32 Practice 脚本 cmm 脚本学习】

请阅读【ARM Coresight SoC-400/SoC-600 专栏导读】 上篇文章【ARM Trace32(劳特巴赫) 使用介绍 2 - Veloce 环境中使用trace32 连接 Cortex-M33】 下篇文章【ARM Trace32(劳特巴赫) 使用介绍 2.2 – TRACE32 进阶命令之 DIAG 弹框命令】 文章目录 1. TRACE32 Practice 语法1.…

Python | 机器学习之PCA降维

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《人工智能奇遇记》&#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 目录结构 1. 机器学习之PCA降维概念 1.1 机器学习 1.2 PCA降维 2. PCA降维 2.1 实验目的 2…

如何在Jupyter Lab中安装不同的Kernel

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

Jupyter Notebook的下载安装与使用教程_Python数据分析与可视化

Jupyter Notebook的下载安装与使用 Jupyter简介下载与安装启动与创建NotebookJupyter基本操作 在计算机编程领域&#xff0c;有一个很强大的工具叫做Jupyter。它不仅是一个集成的开发环境&#xff0c;还是一个交互式文档平台。对于初学者来说&#xff0c;Jupyter提供了友好的界…

linux实现SSH免密登录设置,以及shell脚本实现

原创/朱季谦 最近在搭建linux集群&#xff0c;做了SSH免密登录的设置&#xff0c;正好把过程记录一下&#xff1a; 一.用搭建好的两台虚拟机做演示&#xff0c;A机器&#xff1a;192.168.200.129&#xff0c;B机器&#xff1a;192.168.200.128 二.分别在两台机器上执行以下步…

机器人制作开源方案 | 守护一体化护耆卫士

作者&#xff1a;白玲玲、张硕、孔亚轩单位&#xff1a;兰州理工大学指导老师&#xff1a;毕广利 1. 场景调研 “探索者”平台是结合机械、电子、传感器、计算机软硬件、控制、人工智能和造型技术等众多的先进技术研发推出的专业型机器人设备原型设计工具&#xff0c;包含机构…

利用jquery对HTML中的名字进行替代

想法&#xff1a;将网页中经常要修改的名字放在一个以jquery编写的js文件中&#xff0c;如果需要修改名字&#xff0c;直接修改js文件中的名字即可。 新建name_07.html文件&#xff0c;写入下面的代码&#xff1a; <!DOCTYPE html> <html> <head><meta …

Excel Unix时间戳和日期时间格式的相互转换

时间戳转日期时间 ((A18*3600)/86400)DATE(1970,1,1) # 或 (A18*3600)/8640070*36519# 带格式化 TEXT((C18*3600)/8640070*36519,"yyyy-mm-dd hh:mm:ss")首先加8小时进行时区转换&#xff0c;然后转换成天数&#xff0c;再加上1970年1月1日&#xff0c;最后设置日期…

kafka分布式安装部署

1.集群规划 2.集群部署 官方下载地址&#xff1a;http://kafka.apache.org/downloads.html &#xff08;1&#xff09;上传并解压安装包 [zhangflink9wmwtivvjuibcd2e package]$ tar -zxvf kafka_2.12-3.3.1.tgz -C ../software/&#xff08;2&#xff09;修改解压后的文件…

休闲娱乐 - 挂耳咖啡

公司有一个小的茶歇间&#xff0c;平时去喝个咖啡、放松身心、锻炼下身体。咖啡机是现磨咖啡豆的&#xff0c;喝喝就习惯了。 而我旁边一位同事习惯每天早上来自己泡一杯挂耳咖啡&#xff0c;再配上牛奶&#xff0c;感觉挺高级的。 关于挂耳咖啡就查了一下资料&#xff0c;介绍…

DDR SDRAM 学习笔记

一、基本知识 1.SDRAM SDRAM : 即同步动态随机存储器&#xff08;Synchronous Dynamic Random Access Memory&#xff09;, 同步是指其时钟频率与对应控制器&#xff08;CPU/FPGA&#xff09;的系统时钟频率相同&#xff0c;并且内部命令 的发送与数据传输都是以该时钟为基准…

web环境实现一键式安装启动

部署的痛点 一般在客户环境安装web环境&#xff0c;少说需要花费1-2小时。一般需要安装jdk、nginx、mysql、redis等 等你接触到了inno setup &#xff0c;你有可能会节约更少的时间去部署。也有可能是一个不懂技术的人&#xff0c;都可以进行操作的。废话不多说&#xff0c;接…

Ansible 企业实战详解

一、ansible简介1. ansible是什么2.ansible的特点ansible的架构图 二、ansible 任务执行1、ansible 任务执行模式2、ansible 执行流程3、ansible 命令执行过程 二 .Ansible安装部署1.yum安装2.ansible 程序结构3、ansible配置文件查找顺序4、ansible配置文件5.ansible自动化配置…

yolov5模型代码怎么修改

yaml配置文件 深度乘积因子 宽度乘积因子 所有版本只有这两个参数的不同&#xff0c;s m l x逐渐加宽加深 各种类型层参数对照 backbone里的各层&#xff0c;在这里解析&#xff0c;只需要改.yaml里的各层参数就能控制网络结构 修改网络结构 第一步&#xff1a;把新加的模块…