线程池学习(通俗易懂)

线程池

  • 线程池是什么
  • ThreadPoolExecutor
  • 模拟实现线程池
  • 结语

线程池是什么

假设我们要频繁的创建线程和销毁线程,但是创建线程和销毁线程是有成本的.
所以我们可以提前创建一批线程,后面需要使用的时候,直接拿就可以了,这就是线程池.
当线程不再使用的时候,就归还到池子里.

为什么从线程池里取比在系统里创建线程更加高效呢?

用系统去创建线程,需要调用系统api,进一步有系统内核完成线程的创建.
(内核是给所有线程提供服务的,这是不可控的)
如果是从线程池里取,上述在内核里的操作都已经提前做好了,取线程的过程,就变为了纯用户态(可控).

在java标准库中,也提供现成的线程池供我们使用.

public static void main(String[] args) {
        // Executors: 工厂类   newFixedThreadPool(int): 工厂方法
        // 工厂模式: 一般创建对象都是通过new来调用构造方法
        // 创建了一个固定数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(4);
        // 创建一个线程树木动态变化的线程池
        //Executors.newCachedThreadPool();
        // 创建单个线程(比原本系统内核创建线程更简单)
        //Executors.newSingleThreadExecutor();
        // 创建计时器线程.可能是由多个线程共同执行所有的任务
        //Executors.newScheduledThreadPool(2);

        for (int i = 0; i < 20; i++) {
            service.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello Executors");
                }
            });
        }
    }

// 这里可能会用创建的全部线程去执行,打印20个hello Executors.

什么是工厂模式呢?

一般创建对象都是通过new来调用构造方法,但是构造方法的名字固定就是类名,
有的类就需要多种不同的构造方法,因为构造方法的名字固定,就只能使用方法重载来实现了.
可是还有不能使用方法重载的场景,比如数学中的一个坐标点,
可以使用笛卡尔坐标系的方式,也可以使用极坐标的方式,它们参数的个数和类型相同,无法
构成重载.
当我们使用工厂模式时,不用使用构造方法了,使用普通的方法来构造对象,这样方法的名字就可以是任意的了.由于普通方法的目的是创建对象,这样的方法一般是静态的.

ThreadPoolExecutor

除了上述的线程池之外,标准库还提供了接口更丰富的线程池类: ThreadPoolExecutor.
我么来看看java文档中ThreadPoolExecutor的构造方法,并来学习线程池构造方法的参数和含义.
在这里插入图片描述

  • int corePoolSize : 核心线程数, 在ThreadPoolExecutor里面的线程个数,并非是固定不变的,会根据当前任务的情况动态发生变化,至少得有corePoolSize 线程,哪怕线程池中一点任务也没有.
  • int maximumPoolSize: 最大线程数: maximumPoolSize表示最多的线程数,不能比这个数目更多了.
  • long keepAliveTime, TimeUnit unit : 分别表示时间和单位, 比如3000, ms, 这时就是3s.当线程超过制定时间阈值后就可以销毁了.
  • BlockingQueue workQueue: 线程中有很多任务,这些任务可以用阻塞队列来管理.
  • ThreadFactory threadFactory: 工厂模式,通过这个工厂类来创建线程.
  • RejectedExecutionHandler handler(非常重要,重点掌握): 拒绝方式/拒绝策略.我们知道,线程池中有一个阻塞队列,当阻塞队列满的时候,继续添加任务,该如何应对???
    (1) ThreadPoolExecutor.AbortPolity: 直接抛出异常,线程池就不干活了.
    (2) ThreadPoolExecutor.callerRunsPolity : 谁是添加这个新任务的线程,谁就去执行这个任务.
    (3) ThreadPoolExecutor.DiscardOldestPolity: 丢弃最早的任务,执行新的任务.
    (4) ThreadPoolExecutor.DiscardPolity: 直接把新的任务丢弃掉.

模拟实现线程池

这里我们实现一个固定数量的线程池:

class MyThreadPool {
    private final BlockingDeque<Runnable> queue = new LinkedBlockingDeque<>();
    // 添加任务
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }

    // 创建一个固定数量的线程池
    public MyThreadPool(int n) {
        for (int i = 0; i < n; i++) {
            Thread thread = new Thread(() -> {
                while (true) {
                    try {
                        Runnable runnable = queue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            thread.start();
        }
    }
}
public class Demo26 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool pool = new MyThreadPool(4);
        for(int i = 0; i < 1000; i++)  {
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello pool");
                }
            });
        }
    }
}

在我们创建线程池的时候,线程个数是哪来的?

  1. 有的线程的工作是"CPU密集型", 线程的工作全是运算.大部分的工作是在CPU上完成的,CPU得给它安排核心去完成工作才可以有进展.如果CPU是N个核心,当线程数量也是N的时候.这是理想情况,每个核心上一个线程.如果有很多的线程,就会阻塞等待.
  2. 有的线程是的工作,是"IO密集型", 会涉及大量的等待时间,就算线程数量多一点,也不会给CPU造成太大的负担.

在实际开发中,往往通过尝试不同的线程数,来找到合适的线程数,找到性能和系统资源开销比较均衡的数值.

结语

本篇博客总结了线程池相关的知识,满满的干货,希望有收获的小伙伴多多支持!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2.8 构建gradle环境

构建gradle环境 目录一、安装Java Development Kit (JDK)二、下载Gradle三、解压Gradle四、配置环境变量五、验证安装六、gradle构建java项目七、eclipse配置gradle 目录 一、安装Java Development Kit (JDK) Gradle是基于Java开发的&#xff0c;所以首先需要安装JDK。你可以从…

Dragonfly Topology 蜻蜓网络拓扑

蜻蜓拓扑 Dragonfly Topology 1. 拓扑参数2. Topology Description 拓扑描述3. Topology Variations 拓扑变体 蜻蜓拓扑 Dragonfly Topology 1. 拓扑参数 Dragonfly拓扑参数&#xff1a; N N N: 网络中终端(terminal)的总数量 p p p: 连接到每个路由器的终端数量 a a a: 每…

类加载子系统

一.类加载子系统的作用 类加载子系统&#xff08;Class Loading Subsystem&#xff09;是Java虚拟机的一个重要组成部分&#xff0c;它将Class文件加载到内存中。不同语言程序可以使用编译器生成符合JVM规范的Class文件&#xff0c;Class文件要求在文件开头有特定的文件标识。…

湖仓一体(Lakehouse)架构的核心组件之存储层——Lakehouse 架构(三)

文章目录 前言Lakehouse 存储关键概念行存储与列存储基于存储的查询性能优化 Lakehouse 存储组件云储存文件格式Apache ParquetApache ORCApache Avro相似点和差异点 表格格式Apache HiveIceberg特性和优点 Apache Hudi特性和优点 Delta Lake特性和优点 相似点和差异点 总结 前…

李沐51_序列数据——自学笔记

1.时序模型中&#xff0c;当前数据跟之前观察到的数据相关 2.自回归模型使用自身过去数据来预测未来 3马尔可夫模型假设当前只跟最近少数数据相关&#xff0c;从而简化模型 4.潜变量模型使用潜变量来概括历史信息 生成一些数据&#xff1a;使用正弦函数和一些可加性噪声来生…

【OpenGL开发】PyQt在关闭应用程序时没有运行析构函数的问题

PyQt在关闭应用程序时没有运行析构函数的问题 目录 一、说明二、python的析构函数三、通用版QT5 存在一些问题四、python版PyQt5 存在一些问题五、OpenGL的析构问题5.1 OpenGL很脆弱,这不是危言耸听5.2 以上OpenGL问题解决方法六、一些保留意见一、说明 应用QT做程序界面,在…

工业自动化的未来:UWB模块驱动下的生产优化

随着工业自动化技术的不断发展&#xff0c;UWB技术正在成为工业生产优化的关键驱动力之一。UWB模块作为UWB技术的核心组成部分&#xff0c;具有精准定位、抗干扰、快速响应等特点&#xff0c;为工业自动化提供了新的解决方案和发展机遇。本文将探讨UWB模块驱动下的工业生产优化…

ug钣金展开图教程

UG钣金件如何展平,ug钣金展开步骤&#xff1f;一起来探讨。 第一步:准备一个钣金零件图档(如下图)我们需要进入UG开始-所以应用模块-钣金-NX钣金。 要把这个零件转化为钣金零件&#xff0c;这样才可以进行下一步骤展开。选择转化为钣金&#xff0c;在基本面选择任意一个零件表…

Mysql基础知识学习总结

文章目录 一.数据库的相关概念二.关系模型的核心概念三.SQL分类四.SQL语句注释命令行连接Mysql客户端desc基本用法输出解释示例 show基本用法显示数据库列表显示当前数据库中的表显示表的列信息显示表的索引信息显示服务器状态和系统变量显示创建表的SQL语句显示当前的权限 sel…

遇事不决 量子力学?

文章目录 引入量子力学产生的必然性量子力学名称的由来粒子&#xff1f;波&#xff1f;波粒二象性测不准原理 &#xff08;不确定原理&#xff09;叠加态原理 量子纠缠态叠加量子纠缠量子纠缠实验 逻辑判断&#xff0c;量子力学到底完善吗观测量子纠缠&#xff1f;那我们宏观世…

idea中停止运行Vue

在里面敲入Ctrlc 输入y确定即可。

音频调试(2)

前言&#xff1a; 大家好&#xff0c;今天继续分享记录一下最近的音频调试心得&#xff01;同时这个过程中&#xff0c;也有朋友过来交流音频的问题&#xff0c;通过交流&#xff0c;也是学习到了新东西&#xff01; 视频和音频复合推流&#xff1a; 在上一篇文章里面有提到fdk…

如何批量在文件名前加序号?文件增加序号前缀的方法 四个方法推荐给你

在日常工作和生活中&#xff0c;我们经常需要处理大量的文件&#xff0c;为了方便管理和查找&#xff0c;给文件增加序号前缀是一种常见的做法。下面将介绍几种给文件增加序号前缀的方法&#xff0c;帮助您更高效地整理文件。 方法一&#xff0c;使用【汇帮批量重命名】添加序…

on display 与on show的区别 展示 Show也用来表达电视节目。”秀” 就是Show的音译。

on show 表演&#xff0c;秀&#xff0c;吸引别人过来看 The most wonderful piece of machinery on show was Nasmyth’s steam hammer. on display 只展示&#xff0c; 有可能他人不会注意 Many colorful clothes are displayed in the showcase. 展示柜里摆放着很多鲜艳…

ASV1000视频监控中心录像服务器,实现存满天数,自动覆盖

目录 一、需求 二、需求分析 &#xff08;一&#xff09;录像存满自动覆盖的定义 &#xff08;二&#xff09;录像存满指定的天数自动覆盖 三、录像存满天数后自动覆盖在AS-V1000的实现 &#xff08;一&#xff09;要考虑的问题 1、确定存储需求 2、设置录像计划 3、配…

ts封装axios遇到的问题

在使用ts封装axios时&#xff0c;这里总是有一个报错&#xff0c;经过一顿搜索&#xff0c;发现错误原因&#xff1a;在TypeScript中&#xff0c;函数的参数类型和返回类型是不等价的&#xff0c;因此导致类型不匹配。 解决方法&#xff1a;将 AxiosRequestConfig 改成 Interna…

2024年第二届“天洑杯”数据建模大赛赛题答疑

“天洑杯”数据建模大赛旨在促进数据分析建模技术的推广和发展&#xff0c;培养一批具有用数据思维解决复杂问题能力的优秀人才&#xff0c;推动高水平人工智能交叉学科人才队伍建设。大赛鼓励参赛选手立足于自身的专业背景和工程实践技能&#xff0c;结合数据科学与人工智能相…

vscode运行 .md文件

1、安装插件 markdown-preview-enhancedmarkdown-all-in-oneMarkdown Preview Mermaid Support paste image LimfxCodeEx Code Spell checker 2、项目文件导入即可 本文为学习笔记&#xff0c;所参考文章均已附上链接&#xff0c;若有疑问请私信&#xff01; 创作不易&#x…

LeetCode-热题100:114. 二叉树展开为链表

题目描述 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。展开后的单链表应该与二叉树 先序遍历 顺序相同。 示例…

原子的内部结构

原子非常神奇&#xff0c;花时间思考它是非常有价值的。尽管传统的太阳系示意图存在致命的缺点&#xff0c;但我们还是可以局部应用于原子。 首先&#xff0c;原子与太阳系具有相似性一原子的中心质量大&#xff0c;外部质量小。我们用最简单的氢原子做分析&#xff0c;氢原子…