【多线程初阶】多线程案例之线程池

news2024/11/29 2:36:23

文章目录

  • 前言
  • 1. 什么是线程池
    • 1.1 线程池的优势
  • 2. 标准库中的线程池
    • 2.1 聊聊工厂模式
    • 2.2 Executors 创建线程池的几种方式
    • 2.3 ThreadPoolExecutor 构造方法中的几个参数
      • 2.3.1 RejectedExecutionHandler handler 的几个拒绝策略
  • 3. 自己实现一个线程池
  • 总结


前言

本文主要给大家讲解多线程的一个重要案例 — 线程池.

关注收藏, 开始学习吧🧐


1. 什么是线程池

在讲解线程池是什么之前, 我们先简单聊一聊 “池” 的概念, 在我们学习中, “池” 是一个非常重要的思想方法, 之前听过有内存池, 进程池, 连接池, 常量池等等, 这里的 “池” 其实本质概念上都是一样的.

那么什么是 “池” 呢, 不站在道德层面上来讲, 其实就是我们常说的鱼塘, 鱼塘里都是鱼, 也就是常听到的 “备胎”, 这样就容易理解了吧? 同时和池子里的多个目标搞暧昧, 也就是扩大备胎池, 是不是在某种意义上就提高了谈恋爱的效率呢.

在这里我们的线程池也是一样的, 如果我们只创建销毁一个线程的话, 成本可能并不高, 当我们需要频繁的创建 / 销毁线程, 此时创建销毁线程的成本就不能被忽视了, 因为数量太多了. 我们就需要线程池了. 我们提前创建好一些线程放在一个池子里, 当我们后续需要使用线程时, 直接从池子里拿即可, 当线程不再使用时, 就放回池子里, 就可以大大减少我们频繁创建 / 销毁线程的成本.

1.1 线程池的优势

  1. 降低资源消耗:减少线程的创建和销毁带来的性能开销。
  2. 提高响应速度:当任务来时可以直接使用,不用等待线程创建
  3. 可管理性: 进行统一的分配,监控,避免大量的线程间因互相抢占系统资源导致的阻塞现象。

那么从线程池里取, 就比从系统这里创建线程更高效吗?

  • 如果是从系统这里创建线程, 需要调用系统API, 进一步的由操作系统内核来完成线程的创建过程, 而我们操作系统中的内核是给所有的进程提供服务的, 在这里是不可控的.
  • 如果是从线程池这里获取线程, 上述的内核中进行的操作, 已经提前做好了, 作为用户, 我们只需要去池子里拿即可, 是纯用户态的, 也是用户自己可控的.

2. 标准库中的线程池

在 Java 标准库中, 也提供了现成的线程池.

  • 使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池.
  • 返回值类型为 ExecutorService
  • 通过 ExecutorService.submit 可以注册一个任务到线程池中.
public class ThreadDemo22 {
    // 线程池
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
        // 注册 1000 个任务到线程池中
        for (int i = 0; i < 1000; i++) {
            service.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello");
                }
            });
        }
    }
}

可以看到, 在创建线程池时, 并不是使用 new 一个对象来进行实例化的. 使用的是一个工厂方法 Executors.newFixedThreadPool(), 而 Executors 则是工厂类.

我们来简单谈谈设计模式中的一个经典模式 ---- 工厂模式.

2.1 聊聊工厂模式

顾名思义, 工厂就是用来生产的, 是用来生产对象的, 一般我们创建对象时, 都是使用 new, 通过构造方法来实例化一个对象, 但其实 Java 中的构造方法, 存在一个问题.

构造方法的名字固定就是类名, 而有的类, 需要有多种不同的构造方式, 但是构造方法名字又是固定的, 就只能使用方法重载的方式来实现了 (方法名相同, 参数个数和类型不同). 这里我给大家举个例子.

当我们想要描述一个点时, 我们想按照两种方式进行构造, 一种是按照笛卡尔坐标构造(提供 x, y), 一种是按照极坐标系构造 (提供距离坐标原点距离 r, 以及点与原点连线和 x 轴形成的角度 a), 这两种构造方式, 参数的个数和类型是一样的, 就无法构成重载.

class Point {
    public Point(double x, double y) {}
   
    public Point(double r, double a) {}
}

方法名相同, 参数个数和类型也相同, 无法构成重载.

此时就可以使用工厂模式来解决以上问题, 不使用构造方法, 而是使用普通的方法来构造对象, 这样方法名字就可以是任意的了. 在普通方法内部, 再来 new 对象, 要注意, 这里的普通方法目的是为了创建出对象来, 所以工厂方法一般都得是静态的.

// 工厂模式
class Point {
	// 工厂方法
	public static makePointXY(double x, double y) { 
		// new进行实例化对象
	}
	// 工厂方法
	public static makePointRA(double r, double a) {
		// new进行实例化对象
	}
} 

2.2 Executors 创建线程池的几种方式

我们继续来讲解标准库中的线程池, Executors 主要有以下几种创建的方法.

  • newFixedThreadPool(): 创建一个固定线程数的线程池.
  • newCachedThreadPool(): 创建线程数目动态增长的线程池.
  • newSingleThreadExecutor(): 创建只包含单个线程的线程池.
  • newScheduledThreadPool(): 设定延迟时间后执行命令,或者定期执行命令. 可以看成是进阶版的定时器 Timer.

其实 Executors 本质上是对 ThreadPoolExecutor 类进行的封装. ThreadPoolExecutor 提供了更多的可选参数 (接口更加丰富), 可以进一步细化线程池行为的设定, 更好的满足开发时的实际需求.

2.3 ThreadPoolExecutor 构造方法中的几个参数

这个是 ThreadPoolExecutor 类中参数最全的一个构造方法.
在这里插入图片描述

  • int corePoolSize , int maximumPoolSize: 前者代表核心线程数, 后者代表最大线程数. ThreadPoolExecutor 里面的线程个数, 并非是固定不变的, 会根据当前任务的情况自适应动态变化. 核心线程数表示, 至少得有这些线程, 即使线程池中一点任务也没有. 而最大线程数则表示, 最多不能超过这些线程, 即使线程池中任务已经很多了, 忙不过来了, 也不能比这个数目多. 这样可以做到, 既能保证繁忙的时候可以高效处理任务, 又能保证空闲的时候不会浪费多余资源.
  • long keepAliveTime , TimeUnit unit: 前者表示当没有任务时, 允许线程空闲的最大时间, 空闲时间超过指定值, 线程就可以被销毁了. 后者表示该空闲等待时间的单位.
  • BlockingQueue<Runnable> workQueue: 线程池内部有很多任务, 这些任务, 可以使用一个阻塞队列来管理. 线程池可以内置阻塞队列, 也可以自己手动指定一个.
  • ThreadFactory threadFactory: 工厂模式, 通过这个工厂类来创建线程.
  • RejectedExecutionHandler handler: 拒绝方式 / 拒绝策略, 是线程池考察的重点. 当线程池中阻塞队列满了之后, 在继续添加任务时, 该如何应对.

2.3.1 RejectedExecutionHandler handler 的几个拒绝策略

  • AbortPolicy: 直接抛出异常, 线程池中所有任务都不执行了.
  • CallerRunsPolicy: 谁是添加这个新的任务的线程, 谁就去执行这个任务.
  • DiscardOldestPolicy: 丢弃掉最早的任务, 执行新的任务.
  • DiscardPolicy: 将添加的这个新的任务直接丢弃.

上面我们谈到的线程池, 一组是被封装过的 (Executors), 一组是原生的 (ThreadPoolExecutor), 在开发过程中, 用哪个都可以, 主要是看公司要求, 以及实际需求.

3. 自己实现一个线程池

  • 核心操作为 submit, 将任务加入线程池中.
  • 使用一个 BlockingQueue 组织所有的任务.
  • 创建 n 个线程, 每个线程要做的事情: 不停的从 BlockingQueue 中取任务并执行.
class MyThreadPool {
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();

    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }

    public MyThreadPool(int n) {
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(() -> {
                while (true) {
                    try {
                        Runnable runnable = queue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });

            t.start();
        }
    }
}

总结

✨ 本文主要讲解了什么是线程池, 使用了标准库中的线程池, 简单聊了工厂模式, 以及线程池中的几个参数, 最后自己实现了一个线程池.
✨ 想了解更多的多线程知识, 可以收藏一下本人的多线程学习专栏, 里面会持续更新本人的学习记录, 跟随我一起不断学习.
✨ 感谢你们的耐心阅读, 博主本人也是一名学生, 也还有需要很多学习的东西. 写这篇文章是以本人所学内容为基础, 日后也会不断更新自己的学习记录, 我们一起努力进步, 变得优秀, 小小菜鸟, 也能有大大梦想, 关注我, 一起学习.

再次感谢你们的阅读, 你们的鼓励是我创作的最大动力!!!!!

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

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

相关文章

【COMP304 LEC3】

LEC 3 1. Contingent Formulas&#xff1a; 定义&#xff1a;Truth or falsity of a propositional formula depends on the truth/falsity of the atoms in the formula 例子&#xff1a;p ∧ q is true if both p and q are true, false otherwise.这里p和q就是atoms&…

[React] 性能优化相关 (一)

文章目录 1.React.memo2.useMemo3.useCallback4.useTransition5.useDeferredValue 1.React.memo 当父组件被重新渲染的时候&#xff0c;也会触发子组件的重新渲染&#xff0c;这样就多出了无意义的性能开销。如果子组件的状态没有发生变化&#xff0c;则子组件是不需要被重新渲…

计算机网络笔记 第二章 物理层

2.1 物理层概述 物理层要实现的功能 物理层接口特性 机械特性 形状和尺寸引脚数目和排列固定和锁定装置 电气特性 信号电压的范围阻抗匹配的情况传输速率距离限制 功能特性 -规定接口电缆的各条信号线的作用 过程特性 规定在信号线上传输比特流的一组操作过程&#xff0…

论文研读 - share work - QPipe:一种并行流水线的查询执行引擎

QPipe&#xff1a;一种并行流水线的查询执行引擎 QPipe: A Simultaneously Pipelined Relational Query Engine 关系型数据库通常独立执行并发的查询&#xff0c;每个查询都需执行一系列相关算子。为了充分利用并发查询中的数据扫描与计算&#xff0c;现有研究提出了丰富的技术…

进程之间的通信方式(共享存储,消息传递,管道通信)

进程通信 进程间通信&#xff08;Inter-Process Communication&#xff0c;IPC&#xff09;是指两个进程之间产生数据交互。进程是分配系统资源的单位(包括内存地址空间)&#xff0c;因此各进程拥有的内存地址空间相互独立。为了保证安全&#xff0c;一个进程不能直接访问另一…

计算机网络学习易错点

目录 概述 1.internet和Internet的区别 2.面向连接和无连接 3.不同的T 4.传输速率和传播速率 5.传播时延和传输时延&#xff08;发送时延&#xff09; 6.语法&#xff0c;语义和同步 一.物理层 1.传输媒体与物理层 2.同步通信和异步通信 3.位同步&#xff08;比特同…

【算法分析与设计】贪心算法(下)

目录 一、单源最短路径1.1 算法基本思想1.2 算法设计思想1.3 算法的正确性和计算复杂性1.4 归纳证明思路1.5 归纳步骤证明 二、最小生成树2.1 最小生成树性质2.1.1 生成树的性质2.1.2 生成树性质的应用 2.2 Prim算法2.2.1 正确性证明2.2.2 归纳基础2.2.3 归纳步骤2.3 Kruskal算…

debian设置允许ssh连接

解决新debian系统安装后不能通过ssh连接的问题。 默认情况下&#xff0c;Debian系统不开启SSH远程登录&#xff0c;需要手动安装SSH软件包并设置开机启动。 > 设置允许root登录传送门&#xff1a;debian设置允许root登录 首先检查/etc/ssh/sshd_config文件是否存在。 注意…

TFT LCD刷新原理及LCD时序参数总结(LCD时序,写的挺好)

cd工作原理目前不了解&#xff0c;日后会在博客中添加这一部分的内容。 1.LCD工作原理[1] 我对LCD的工作原理也仅仅处在了解的地步&#xff0c;下面基于NXP公司对LCD工作原理介绍的ppt来学习一下。 LCD(liquid crystal display,液晶显示屏) 是由液晶段阵列组成&#xff0c;当…

【EasyPoi】SpringBoot使用EasyPoi自定义模版导出Excel

EasyPoi 官方文档&#xff1a;http://doc.wupaas.com/docs/easypoi Excel模版导出 引入依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency…

newstarctf

wp web: 1.rce 可以发现这个变量名有下划线也有点。 $code$_POST[e_v.a.l]; 这时候如果直接按这个变量名来传参&#xff0c;php 是无法接收到这个值的&#xff0c;具体原因是 php 会自动把一些不合法的字符转化为下划线&#xff08;注&#xff1a;php8以下&#xff09;&am…

【论文极速读】IMAGEBIND —— 通过图片作为桥梁桥联多模态语义

【论文极速读】IMAGEBIND —— 通过图片作为桥梁桥联多模态语义 FesianXu 20230929 at Baidu Search Team 前言 当前大部分多模态工作都集中在图片-文本、视频-文本中&#xff0c;关于音频、深度图、热值图的工作则比较少&#xff0c;在IMAGEBIND中&#xff0c;作者提出了一种…

Neo4j最新安装教程(图文版)

目录 一、软件介绍 二、下载软件 1、官方下载 2、云盘下载 三、安装教程 1、首先配置Neo4j的环境变量 2、启动neo4j服务器 3、访问界面 一、软件介绍 官网地址&#xff1a;https://neo4j.com/ Neo4j是一个高性能、可扩展的图数据库管理系统。它专注于存储、查询和处理大…

Explain执行计划字段解释说明---ID字段说明

ID字段说明 1、select查询的序列号,包含一组数字&#xff0c;表示查询中执行select子句或操作表的顺序 2、ID的三种情况 &#xff08;1&#xff09;id相同&#xff0c;执行顺序由上至下。 &#xff08;2&#xff09;id不同&#xff0c;如果是子查询&#xff0c;id的序号会…

大数据Flink(九十四):DML:TopN 子句

文章目录 DML:TopN 子句 DML:TopN 子句 TopN 定义(支持 Batch\Streaming):TopN 其实就是对应到离线数仓中的 row_number(),可以使用 row_number() 对某一个分组的数据进行排序 应用场景

cesium 雷达扫描 (扫描线)

cesium 雷达扫描 (扫描线) 1、实现方法 图中的线使用polyline方法绘制,外面的圆圈是用ellipse方法绘制(当然也不指这一种方法),图中线的扫描转动效果是实时改变线的经纬度来实现(知道中心点经纬度、又已知方向和距离可以求出端点的经纬度)使用CallbackProperty方法来实…

数据结构与算法——18.avl树

这篇文章我们来看一下avl树 目录 1.概述 2.AVL树的实现 1.概述 我们前面讲了二叉搜索树&#xff0c;它是有一个key值&#xff0c;然后比父节点key值大的在左边&#xff0c;小的在右边。这样设计是为了便于查找。但是有一种极端的情况&#xff0c;就是所有的结点都在一边&am…

7.3 调用函数

前言&#xff1a; 思维导图&#xff1a; 7.3.1 函数调用的形式 我的笔记&#xff1a; 函数调用的形式 在C语言中&#xff0c;调用函数是一种常见的操作&#xff0c;主要有以下几种调用方式&#xff1a; 1. 函数调用语句 此时&#xff0c;函数调用独立存在&#xff0c;作为…

艺术表现形式

abstract expressionism 抽象表现主义 20世纪中期的一种艺术运动&#xff0c;包括多种风格和技巧&#xff0c;特别强调艺术家通过非传统和通常非具象的手段表达态度和情感的自由。 抽象表现主义用有力的笔触和滴落的颜料来表达情感和自发性。 简单地结合“abstract expression…

基于Java的服装销售平台设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…