线程池(详解)

目录

前言

线程池的好处

使用Executors 创建常见的线程池 

工厂模式:

往线程池当中添加任务

常见线程类 

​编辑 线程池的参数介绍

线程池的工作流程

补充


前言

如果我们需要频繁的创建销毁线程,此时创建销毁线程的成本,不能忽视了
因此就可以使用线程池.提前创建好一波线程,后续需要使用线程,就直接从“池子”里拿一个即可~
当线程不再使用,就放回池子里~~
本来,是需要创建线程/销毁线程,
现在,是从池子里获取到现成的线程,并且把线程归还到池子中

这样比从系统这里创建线程更快更高效

线程池的好处

降低资源的消耗
       通过重复利用已经创建的线程降低线程创建,销毁造成的开销。

       我们知道,传统的操作当中,通过调用thread.start()来创建线程,这一个步骤,实际上是调用了操作系统提供的api,来创建PCB,这一个过程涉及操作系统内部的系统调用。

       当线程执行完自己的任务之后,重新销毁线程。这个销毁的过程,也是交给操作系统完成的。

 而如果使用了线程池,可以有效减少不断创建、销毁线程带来的损耗。

      因为线程池创建之后,每次执行任务,都是从线程池当中获取线程,来执行任务,不用每次都通过调用api来获取。

提高响应速度
         当任务需要执行的时候,任务可以不需要等待到线程创建,就可以立即执行任务。

      提高线程的可管理能力
         线程属于稀缺资源,如果无限制地创建线程,不仅仅会消耗系统资源,还会降低系统的稳定性。       

         如何理解所谓的"稳定性"呢?

         相比于把线程的获取交给操作系统内核来完成,通过线程池直接获取线程,可以提高程序的"可控性"。

         因为,如果直接创建线程,程序执行的时候,无法预知操作系统的内核当中究竟背负了多少的任务。操作系统内核也许背负了成千上万的任务......那轮到我需要执行的"创建线程"这个任务的时候,究竟响应速度怎样,能否响应都是问题......

         这样,也就无法预知通过操作系统获取线程的真实情况,也就提高了管理线程的难度


如果是从系统这里创建线程,需要调用系统api,进一步的由操作系统内核,完成
线程的创建过程(内核是给所有的进程提供服务的)不可控的!!


如果是从线程池这里获取线程,上述的内核中进行的操作,都提前做好了,现在是可控的!!
取线程的过程,纯粹的用户代码完成(纯用户态)可控的!!

使用Executors 创建常见的线程池 

此处,4指定的是线程池当中的核心最大线程数量,后面会提及。

        线程池创建线程的方式,是通过"工厂模式"来实现的,是通过一个工厂来实现的。

        这里创建线程对象,并没有直接new N个线程对象,而是通过一个特定的工厂(ThreadFactory)来生产线程,后面会介绍到。

我们Java有方法重载的机制,单当出现下面这种情况时,就不行了。

接下来,就可以用工场来解决这件事了。

工厂模式:

       可以简单地理解为:使用普通方法,代替构造方法来创建对象。

       如果想要获取对象,只能通过这个"普通方法"来获取对象。

        并且,可以通过这个"工厂"来获取多个对象。

往线程池当中添加任务

         在①当中,已经创建好10个线程的任务了,那么,线程的任务,如果执行呢?

         此时,就需要使用submit()方法来把任务放到线程池当中,如下代码:

 

上图代码的含义是,往一个线程池当中初始化4个线程,然后再往这个线程池当中提交10个任务,由4个线程来"平均"分配一下这10个任务。

       但是,不一定是每个线程都可以平均执行100个任务。尽管线程调度有记帐系统来尽可能让每个参与调度的线程执行的时间比较平均,但是也不可以百分百确保每个线程执行获取的任务数量都一模一样。

常见线程类 

 

 常见的

 

 线程池的参数介绍

除了上述这些线程池之外,标准库还提供了一个接口更丰富的线程池类
ThreadPoolExecutor

看原码,

corePoolSize:核心线程数,线程池启动时就会创建的线程数量。即使核心线程是空闲的,也不会被回收,除非

调用了allowsCoreThreadTimeOut方法为true

executorService.allowCoreThreadTimeOut(true);


maximumPoolSize:最大线程数,线程池中最大的线程数量

keepAliveTime:线程超时时间,看源码可知,该参数的意义是线程从工作队列中取出任务的超时时间。其中,timed是指是否设置allowCoreThreadTimeOut为true,如果没有设置,则判断当前线程数是否大于核心线程数。timeout指的就是线程从队列中取任务是否超时。当一个线程从工作队列中取任务时,满足以下条件:(当前线程数大于最大线程数 或 (timed为true且该线程获取任务超时))&(当前线程数大于1 或 工作队列为空(说明不是繁忙时候)),这个线程就会被回收。

unit:超时时间的单位

workQueue:阻塞队列

threadFactory:线程工厂,要实现ThreadFactory接口,线程池创建线程时会调用ThreadFactory的newThread方法创建线程。

RejectedExecutionHandler:饱和策略,详情见下面的说明

如果线程池中的线程数量大于等于最大线程数,且工作队列已满,新提交的任务就会被拒绝,此时就要使用饱和策略。默认饱和策略是AbortPolicy,该策略会抛出rejectedExecution,调用者可以捕获该异常,编写处理代码。除了默认的饱和策略,还有其他几种饱和策略:

    >DiscardPolicy:抛弃策略,会悄悄的抛弃没法加入到队列的任务

    >DiscardOldestPolicy:抛弃最旧策略,会悄悄的抛弃下一个要执行的任务。如果使用的是优先队列,会抛弃优先级最高的任务,最好不要使用。

    >CallerRunsPolicy:调用者运行策略,即不会抛出异常,也不会放弃执行。任务会被调用了execute方法的线程调用。比如,在main方法中开启线程池,如果线程池使用该策略,那么饱和的任务会被主线程调用。

testThreadPoolExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

线程池的工作流程

 

大致流程描述:

执行任务,核心线程数未达到corePoolSize该值,就创建线程。否则塞入任务队列。
任务队列满了,就创建线程,但是总线程数不能超过该值maximumPoolSize。
如果任务队列满了,线程数达到maximumPoolSize值,则执行失败策略。
工作线程则不停的轮询去队列中poll任务,如果poll为空,则工作线程执行结束(回收线程)。
如果工作线程数<=核心线程数corePoolSize,则使用take从队列中获取任务(核心线程一直await)。

任务队列缓冲
核心方法:

boolean offer(E) 往队列中添加一个元素。如果队列已满,添加失败,返回fasle。
E poll(timeout, unit) 获取并移除队列头节点。如果队列为空,则等待${timeout}时间,如果还是没有数据,则返回null。
E take() 获取并移除队列头节点。如果队列为空,则一直等待。

补充

1、当设置核心线程数为0的时候,会创建一个非核心线程进行执行
2、当设置核心线程数不为0的时候,如果核心线程数在执行,会有一个非核心线程数从队列中取对象执行线程
3、核心线程数执行的是队列的take,非核心线程数执行队列的offer和poll
4、核心线程数不为0且队列为SynchronousQueue时,就成了单线程运行了
 

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

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

相关文章

Vue - 4( 8000 字 Vue 入门级教程)

一&#xff1a; Vue 初阶 1.1 关于不同版本的 Vue Vue.js 有不同版本&#xff0c;如 vue.js 与 vue.runtime.xxx.js&#xff0c;这些版本主要针对不同的使用场景和需求进行了优化&#xff0c;区别主要体现在以下几个方面&#xff1a; 完整版 vs 运行时版&#xff1a; vue.js&…

标注平台工作流:如何提高训练数据质量与管理效率

世界发展日益依托数据的驱动&#xff0c;企业发现&#xff0c;管理不断增长的数据集却愈发困难。数据标注是诸多行业的一个关键过程&#xff0c;其中包括机器学习、计算机视觉和自然语言处理。对于大型语言模型&#xff08;LLM&#xff09;来说尤是如此&#xff0c;大型语言模型…

代码随想录阅读笔记-回溯【组合总和III】

题目 找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数&#xff0c;并且每种组合中不存在重复的数字。 示例 1: 输入: k 3, n 7 输出: [[1,2,4]] 示例 2: 输入: k 3, n 9 输出: [[1,2,6], [1,3,5], [2,3,4]] 说明&#xff1a; 所有数字都是正整数。…

Day30 回溯 LeedCode 332.重新安排行程 51. N皇后 37. 解数独 蓝桥杯 与或异或

332. 重新安排行程 给你一份航线列表 tickets &#xff0c;其中 tickets[i] [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。 所有这些机票都属于一个从 JFK&#xff08;肯尼迪国际机场&#xff09;出发的先生&#xff0c;所以该行程必须从 JFK…

【小程序】常用方法、知识点汇总1

欢迎来到《小5讲堂》 这是《小程序》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言请求超时Markdown解析逐行显示效果文本变动事件转发…

C语言—每日选择题—Day65

前言 我们的刷题专栏又又又开始了&#xff0c;本专栏总结了作者做题过程中的好题和易错题。每道题都会有相应解析和配图&#xff0c;一方面可以使作者加深理解&#xff0c;一方面可以给大家提供思路&#xff0c;希望大家多多支持哦&#xff5e; 第一题 1、如下代码输出的是什么…

LINUX系统触摸工业显示器芯片应用方案--Model4(简称M4芯片)

背景介绍&#xff1a; 触摸工业显示器传统的还是以WINDOWS为主&#xff0c;但近年来&#xff0c;安卓紧随其后&#xff0c;但一直市场应用情况不够理想&#xff0c;反而是LINUX系统的触摸工业显示器大受追捧呢&#xff1f; 触摸工业显示器传统是以Windows系统为主&#xff0c…

无线游戏手柄的测试(Windows11系统手柄调试方法)

实物 1、把游戏手柄的无线接收器插入到电脑usb接口中 2、【控制面板】----【查看设备和打印机】 3、【蓝牙和其它设备】--【更多设备和打印机设置】 4、鼠标右键【游戏控制器设置】 5、【属性】 6、【测试】&#xff08;每个按键是否正常&#xff09; 7、【校准】&#xff08;…

学习笔记:解决拖延

1 解决拖延&#xff0c;减轻压力的关键心态和方法 1.1 要点梳理 拖延是因为自己一直在逃避&#xff0c;重点是要有效突破逃避圈&#xff0c;进入学习圈&#xff0c;扩展成长圈。 毒蛇曲线&#xff08;见思维导图&#xff09;中越是临近截止期限&#xff0c;拖延的焦虑越上升…

【项目实战经验】DataKit迁移MySQL到openGauss(下)

上一篇我们分享了安装、设置、链接、启动等步骤&#xff0c;本篇我们将继续分享迁移、启动~ 目录 9. 离线迁移 9.1. 迁移插件安装 中断安装&#xff0c;比如 kill 掉java进程&#xff08;安装失败也要等待300s&#xff09; 下载安装包准备上传 缺少mysqlclient lib包 mysq…

树的基本概念(重点)

1.一下概念很重要 以下面的的树为例&#xff1a; 1.节点的度&#xff1a; 一个节点所含有的子树的个数就是这个节点的度&#xff0c;注意一个子节点也算一个子树。 如上图&#xff1a; B节点的度1&#xff1b; A节点的度6&#xff1b; 2.树的度&#xff1a; 一个树的度这…

实模式和保护模式

前言 大家好&#xff0c;我是jiantaoyab&#xff0c;内存中的每一个字节都有一个唯一的地址&#xff0c;通过这个地址我们能去取出一个个比特&#xff0c;我们可以称这个过程为寻址。在现实生活中&#xff0c;我们去菜鸟拿快递的时候&#xff0c;是通过取件码上的编号到架子上…

CLion 解决中文输出乱码的问题

问题介绍 在 Clion 的默认设置下&#xff0c;输出中文会出现乱码&#xff0c;如下 #include <iostream> using namespace std;int main() {cout << "你好" << endl;return 0; }输出 浣犲ソProcess finished with exit code 0解决方案 编码问题…

LangChain - Chain

文章目录 1、概览为什么我们需要链? 2、快速入门 (Get started) - Using LLMChain多个变量 使用字典输入在 LLMChain 中使用聊天模型&#xff1a; 3、异步 API4、不同的调用方法__call__调用仅返回输出键值 return_only_outputs只有一个输出键 run只有一个输入键 5、自定义cha…

git submodule---同步最新的内容

0 Preface/Foreword 1 同步最新submodule内容到repo中 项目的repo包含了一个子模块&#xff0c;在开发过程中&#xff0c;经常需要同步子模块最新的commit到repo中。该如何操作呢&#xff1f; 本地在克隆时候&#xff0c;已经同步把子模块中的内容克隆下来了&#xff0c;但是…

Spring 之 IoC概述

目录 1. IoC概述 1.1 控制反转 1.2 依赖注入 2. IoC容器在Spring中的实现 2.1 BeanFactory 2.2 ApplicationContext 2.2.1 ApplicationContext的主要实现类 1. IoC概述 全称&#xff1a;Inversion of Control&#xff0c;译为 “控制反转” Spring通过IoC容器来管理所有…

【LAMMPS学习】八、基础知识(1.6) LAMMPS 与其他代码耦合

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

基于拉格朗日分布算法的电动汽车充放电调度MATLAB程序

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 程序简介 该模型主要做的是基于拉格朗日分布算法的电动汽车充放电调度模型。利用蒙特卡洛模拟法模拟出电动汽车负荷曲线&#xff0c;并求解出无序充电功率曲线和有序充电曲线&#xff0c;该模型在电动汽车个…

标准C库文件操作

open 系列API 和 fopen系列API的区别 1.来源: -open 是UNIX系统调用函数(包括LINUX系统)&#xff0c;返回的是文件描述符 -fopen是ANSIC标准的C语言库函数&#xff0c;在不同系统重调用不同内核的API 2.移植性: fopen 是C标准函数&#xff0c;具有良好的移植性&#xff1b; 而…

JUC-线程的创建、运行与查看

创建和运行线程 Thread创建线程 Thread 创建线程方式&#xff1a;创建线程类&#xff0c;匿名内部类方式 start() 方法底层其实是给 CPU 注册当前线程&#xff0c;并且触发 run() 方法执行线程的启动必须调用 start() 方法&#xff0c;如果线程直接调用 run() 方法&#xff…