当前位置: 首页 > news >正文

多线程代码案例-4 线程池

1、引入

池是一个非常重要的概念,我们有常量池,数据库连接池,线程池,进程池,内存池……

池的作用:

1、提前把要用的对象准备好

2、用完的对象也不立即释放,先留着以备下次使用,提高效率。

一开始讲过,进程能够解决并发编程问题,但是因为频繁创建和销毁进程,需要耗费大量的空间、时间,成本太高了,所以我们引进了轻量级进程——线程

但是如果创建和销毁线程的频率进一步提高,此时线程创建和销毁的开销,也不能忽略了。(抛开剂量谈毒性,都是耍流氓),因此我们需要想办法,优化此处线程创建和销毁的效率

1、引入轻量级线程——纤程/ 协程(Java21引入的虚拟线程就是这个东西)。协程本质,是程序员在用户态的代码中进行调度,而不是靠内核调度器调度,这就节省了很多调度上的开销。

2、线程池把要使用的线程提前创建好,用完了也不直接释放,而是以备下次使用。这样就节省了线程创建/销毁的开销。在这个过程中,没有频繁创建销毁线程,只是从线程池里面,取线程使用,用完了就还给线程池。

那为什么,从线程池里面取线程,就比从系统申请更加高效呢?

举个例子:还是银行的例子,柜台里面,相当于内核态,大堂相当于用户态。

当我们要办理业务,需要一个身份证复印件的时候,我们并没有带,此时,业务员说我们有两个途径解决:

1、自己拿着身份证,去自助复印机上打印即可(纯用户态代码)

2、把身份证交给业务员,他拿着身份证帮你去复印。(业务员拿到我们的身份证之后,就消失在我们的视野之中了,此时我们不知道他要花费多长时间,也不知道他要做哪些事情,我们唯一能做的,就是等……)

结论:如果一个工作,靠用户自己就能完成,就更加可控,更加高效。从线程池里面取线程,是纯用户态代码,更加可控,更加高效 。

如果一个工作,用户拜托业务员完成,就不可控,更低效。通过系统申请创建线程是需要内核来完成的,不可控,更低效。

2、标准库中的线程池

我们可以在Java官方文档中,找到java.util.concurrent这个包,在下面的Classes中就可以找到ThreadPoolExecutor,往下翻就可以找到构造方法,有四种:

我们只需要关注最后一个就可以(最后一种参数最全)。

2.1 构造方法参数

1、int corePoolSize

表示的是核心线程数(一个线程池中,最少得有多少个线程)。

2、int maximumPoolSize

表示的最大线程数(一个线程池中,最多能有多少个线程)。

注意:标准库提供的线程池,持有的线程的个数,并非是一成不变的,会根据当前任务量,自适应线程的个数。(任务非常多,就多搞几个线程;任务比较少,就少搞几个线程)。

举个例子:假设一个公司,里面有10个员工(正式签劳动合同的员工)。当公司业务非常繁忙的时候,10个人就干不过来了,就需要招聘……一个成本比较低的做法:招聘实习生(非正式员工),比如可以再招聘5个实习生(廉价劳动力)

过了一段时间,公司没那么忙了,大家都闲下来开始摸鱼了,10个正式员工,是不能随便裁掉的,但是这5个实习生,是可以随便裁的。把这5个实习生裁掉,当前这10个正式员工也就没有那么空闲了,整体的成本也降低了。

如果过了一段时间,公司业务又多了起来,10个人又忙不过来了,此时再重新招几个实习生进来就好了。

通过实习生来应对突发的峰值!!!

此时,这10个正式员工就是核心线程数,10正式员工+5实习生就是最大线程数了

3、long keepAliveTime

表示的是保持存活时间, 实习生线程空闲时间超过了这个时间限制,就会被销毁掉。实习生线程被销毁掉之后,就没有了,在未来的某一天,线程池还会重新招聘实习生,但已经不是之前那个了。

4、TimeUnit unit

表示的是时间单位(s、min、ms、hour),即指定保持存活时间的时间单位

5、BlockingQueue<Runnable>workQueue

和定时器类似的阻塞队列,用来存放等待执行的任务。当核心线程都在忙碌的时候,新任务就会放入这个队列中排队等待。Runnable用来描述任务的主体。也可以设置为PriorityBlockingQueue,使当前阻塞队列带有优先级。

6、ThreadFactory threadFactory

表示线程工厂。

工厂模式,和单例模式一样,也是一种常见的设计模式。通过专门的“工厂类/工厂对象”来创建指定的对象,本质上是给Java语法填坑的:

举个例子:

我们会发现,上面的代码,无法通过编译,因为这两个构造方法无法构成重载 。

构成重载的条件:

1、方法名字相同

2、新参的个数/类型不同

上面的代码,不符合第二个条件,无法通过编译。为了解决上述问题,就引入了“工厂模式”,使用普通的方法来创建对象,相当于把构造方法封装了一层。

如果把工厂方法放到一个其他的类里面,这个类就叫做“工厂类 ”。

总结:通过静态方法封装new操作,在main方法中无需实例化对象,在方法内部设定不同的属性完成对对象初始化,构造对象的过程,就是工厂模式。

回来看我们的参数:ThreadFactory threadfactory通过这个工厂类,来创建线程对象(Thread对象)在这个类里面提供了方法(也不一定非得是静态的),这些方法封装了newThread操作,并且给Thread设置一些属性,就构成了ThreadFactory(线程工厂)。

7、RejectExecutionHandle handler

这个是其中最重要的参数!!!

这个表示的是拒绝策略。在线程池中,有一个阻塞队列,它能够容纳的元素是有上限的。当任务队列已经满了的时候,如果继续往队列里面添加元素,那么线程池会怎么办呢?这就需要拒绝策略来发挥作用了。

这是官方文档给出的四种拒绝策略:

1、直接抛出异常:当继续添加任务的时候,系统会直接抛出异常,进入“撂挑子”状态。此时,无论是新任务还是旧任务,都停止执行。

2、由调用者执行:新任务由添加任务的线程负责执行,而非线程池执行。 

3、丢弃最老的任务:系统会舍弃最老的任务,然后去执行新添加的任务。

4、丢弃最新的任务:新的任务会直接被抛弃,不再执行。不会调用线程,线程池也不会执行该任务。

3、Executor工厂类

ThreadPoolExecutor本身使用起来是比较复杂的,因此Java标准库还提供了另外一个封装起来的版本——Excutors工厂类。通过这个类来创建出不同的线程池对象(在内部就把ThreadPoolExecutor创建好了并且设置了不同的参数): 可以看到Executors这个工厂类中由许多不同的线程池:

newSingleThreadExecutor():只包含单个线程的线程池。

newScheduleThreadPool():类似于定时器,也能延时执行任务。

newCachThreadPool():是线程数目能够动态扩容的线程池。

newFixedThreadPool():是线程数目固定的线程池。

示例代码:

public class ThreadDemo32 {public static void main(String[] args) {ExecutorService service = Executors.newFixedThreadPool(4);//添加任务service.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello world");}});}
}

打印结果如下:

ThreadPoolExecutor也是通过submit添加任务,只是构造方法不同。

那么什么时候使用Executor什么时候使用ThreadPoolExecutor呢???

当我们只是简单使用一个线程池的时候,就可以使用Executors,而当我们需要一个高度定制化的线程池的时候,就可以使用ThreadPoolExcutor

阿里巴巴的Java开发手册中写道:不建议使用Executors,一定要使用ThreadPoolExecutor,用ThreadPoolExecutor一切尽在掌握之中,不会出现不可控因素。这个我们可以参考,但还是要以具体公司的编程规范为准。

4、如何确定线程池的线程数量

创建线程池的时候,很多时候,需要程序员设定线程池的数量。这个数量怎么设置更合适呢?

网上很多的说法:加入CPU的逻辑核心数是N,线程数量应该是N,N+1,1.5N,2N……这些说法都是错误的。

不同的程序,需要设定的线程的数量是不同的,必须要具体问题具体分析。

我们需要进行区分,当前任务是CPU密集型的任务,还是IO密集型任务。

CPU密集型任务:这个线程池中的大部分线程都要到CPU上运行,进行计算。比如,在线程的run方法中计算1~10w种就是CPU密集型。

 IO密集型任务:这个线程大部分时间都在等待IO(输入:input 输出:output),不需要到CPU上运行,比如,在run方法中,搞一个scanner,读取用户输入,就是IO密集型。

如果线程池中的所有项目都是CPU密集型的,每个线程所有的工作都要到CPU上执行的,此时,线程的数目不应该超过CPU逻辑核心数N。

如果线程池中的所有项目都是IO密集型的,每个线程的所有工作都是等待IO,CPU资源消耗非常少,此时线程的数目就可以远超过CPU逻辑核心数N。

数目的两个场景都是两种非常极端的情况,在日常开发中,一个进程中的线程,一部分是IO密集型,一部分是CPU密集型,这里的比例是不好确定的。

综上,由于程序的复杂性,很难直接对线程的数量进行估计。更合适的做法应该是:通过实验/测试的方式,找到更合适的线程数目。即尝试给线程池,设定不同的线程数目,分别进行性能测试,衡量每种线程数目下的时间开销和资源占用开销,找到两者的合适值。

5、实现一个简单的线程池

我们这里实现的线程池是固定数目的,暂时不考虑线程数目的增多和减少。

大致思路如下:

1、提供构造方法,指定创建多少个线程。

2、在构造方法中,把这些线程创建号。

3、有一个阻塞队列,持有要执行的任务。

4、提供submit方法,用来添加新的任务。

 代码如下:

class MyThreadPoolExecutor{private List<Thread> threadList = new ArrayList<>();//用来保存任务的队列private BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(10);//通过n指定创建多少个线程public MyThreadPoolExecutor(int n){for (int i = 0; i < n; i++) {Thread t = new Thread(()->{while (true) {try {//此处的take是有阻塞功能的//如果此时的队列为空,此处的take就会阻塞Runnable runnable = queue.take();//取出一个任务就执行一个任务runnable.run();} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();threadList.add(t);}}public void submit(Runnable runnable) throws InterruptedException {queue.put(runnable);}
}

构造方法使用while(true),不断循环获取阻塞队列中的元素,并调用该元素的run方法执行这个任务。 

submit方法负责把任务添加到任务队列,起到任务提交的作用。

提供这种方式,线程池能够高效地复用,减少线程创建销毁带来的开销。

测试代码:

在这个代码中我们看到i的下面有一个小小的红色波浪线:这就涉及到我们之前讲过的变量捕获了。 

run这个回调函数访问当前外部作用域的变量就是变量捕获,我们之前讲过,变量捕获的值,要不是final修饰的常量值,要不就得是一个“事实final”的变量,但我们现在的i是一直变化的。

解决方法:只需在循环中将i赋值给n即可:

 运行结果:

从这些结果也可以看出:线程的随即调度/抢占式执行。 

完整代码:

package Thread;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;class MyThreadPoolExecutor{private List<Thread> threadList = new ArrayList<>();//用来保存任务的队列private BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(10);//通过n指定创建多少个线程public MyThreadPoolExecutor(int n){for (int i = 0; i < n; i++) {Thread t = new Thread(()->{while (true) {try {//此处的take是有阻塞功能的//如果此时的队列为空,此处的take就会阻塞Runnable runnable = queue.take();//取出一个任务就执行一个任务runnable.run();} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();threadList.add(t);}}public void submit(Runnable runnable) throws InterruptedException {queue.put(runnable);}
}
public class ThreadDemo33 {public static void main(String[] args) throws InterruptedException {MyThreadPoolExecutor myThreadPoolExecutor = new MyThreadPoolExecutor(4);for (int i = 0; i < 1000; i++) {int n = i;myThreadPoolExecutor.submit(new Runnable() {@Overridepublic void run() {System.out.println("执行任务"+n+",当前线程为:"+Thread.currentThread().getName());}});}}
}

相关文章:

多线程代码案例-4 线程池

1、引入 池是一个非常重要的概念&#xff0c;我们有常量池&#xff0c;数据库连接池&#xff0c;线程池&#xff0c;进程池&#xff0c;内存池…… 池的作用&#xff1a; 1、提前把要用的对象准备好 2、用完的对象也不立即释放&#xff0c;先留着以备下次使用&#xff0c;提…...

JSON Schema 高效校验 JSON 数据格式

在数据交换和API开发中&#xff0c;JSON 已成为最流行的数据格式之一。但你是否遇到过这些困扰&#xff1f; 接收的JSON字段缺失关键数据&#xff1f;数值类型意外变成了字符串&#xff1f;嵌套结构不符合预期&#xff1f; JSON Schema 正是解决这些问题的利器。本文将带你全…...

机器学习09-正规方程

机器学习笔记&#xff1a;正规方程&#xff08;Normal Equation&#xff09; 概述 正规方程是线性回归中求解参数的一种解析方法。它基于最小化损失函数&#xff08;如最小二乘法&#xff09;来直接计算出参数的最优值。在机器学习中&#xff0c;这种方法尤其适用于特征数量不…...

Java大师成长计划之第26天:Spring生态与微服务架构之消息驱动的微服务

&#x1f4e2; 友情提示&#xff1a; 本文由银河易创AI&#xff08;https://ai.eaigx.com&#xff09;平台gpt-4-turbo模型辅助创作完成&#xff0c;旨在提供灵感参考与技术分享&#xff0c;文中关键数据、代码与结论建议通过官方渠道验证。 在现代微服务架构中&#xff0c;服务…...

Linux 文件(1)

1. 文件 1.1 文件是什么 一个文件&#xff0c;是由其文件属性与文件内容构成的。文件属性又称为一个文件的元数据&#xff0c;因此如果一个文件&#xff0c;内容为空&#xff0c;这个文件依然要占据磁盘空间。 1.2 文件在哪里 一个文件&#xff0c;如果没有被打开&#xff…...

程序代码篇---python向http界面发送数据

文章目录 前言 前言 本文简单接受了python向http界面发送数据...

【iOS】探索消息流程

探索消息流程 Runtime介绍OC三大核心动态特性动态类型动态绑定动态语言 方法的本质代码转换objc_msgSendSELIMPMethod 父类方法在子类中的实现 消息查找流程开始查找快速查找流程慢速查找流程二分查找方法列表父类缓存查找 动态方法解析动态方法决议实例方法类方法优化 消息转发…...

院校机试刷题第六天:1134矩阵翻转、1052学生成绩管理、1409对称矩阵

一、1134矩阵翻转 1.题目描述 2.解题思路 很简单的模拟题&#xff0c;甚至只是上下翻转&#xff0c;遍历输出的时候先把最下面那一行输出即可。 3.代码 #include <iostream> #include <vector> using namespace std;int main() {int n;cin >> n;vector&l…...

DeepSeek在简历筛选系统中的深度应用

一、多模态解析引擎的技术突破 1.1 复杂格式的精准解析 针对简历格式多样性挑战,DeepSeek采用三级解析架构: 格式标准化层:基于Transformer的DocParser模型支持200+种文档格式转换视觉特征提取:使用改进的YOLOv8进行证书印章识别(mAP@0.5达93.7%)语义重构模块:通过注意…...

c++多线程debug

debug demo 命令行查看 ps -eLf|grep cam_det //查看当前运行的轻量级进程 ps -aux | grep 执行文件 //查看当前运行的进程 ps -aL | grep 执行文件 //查看当前运行的轻量级进程 pstree -p 主线程ID //查看主线程和新线程的关系 查看线程栈结构 pstack 线程ID 步骤&…...

【回溯 剪支 状态压缩】# P10419 [蓝桥杯 2023 国 A] 01 游戏|普及+

本文涉及知识点 C回溯 位运算、状态压缩、枚举子集汇总 P10419 [蓝桥杯 2023 国 A] 01 游戏 题目描述 小蓝最近玩上了 01 01 01 游戏&#xff0c;这是一款带有二进制思想的棋子游戏&#xff0c;具体来说游戏在一个大小为 N N N\times N NN 的棋盘上进行&#xff0c;棋盘…...

CUDA 纹理入门

一、什么是CUDA纹理 CUDA纹理是NVIDIA GPU提供的一种特殊内存访问机制,它允许高效地访问和过滤结构化数据。纹理内存最初是为图形渲染设计的,但在通用计算(GPGPU)中也很有用。 二、纹理内存的优势 缓存优化:纹理内存有专用的缓存,适合空间局部性好的访问模式 硬件过滤:支…...

大模型微调步骤整理

在对深度学习模型进行微调时,我通常会遵循以下几个通用步骤。 第一步是选择一个合适的预训练模型。PyTorch 的 torchvision.models 模块提供了很多经典的预训练模型,比如 ResNet、VGG、EfficientNet 等。我们可以直接使用它们作为模型的基础结构。例如,加载一个预训练的 Re…...

【GPT入门】第39课 OPENAI官方API调用方法

【GPT入门】第39课 OPENAI官方API调用方法 1. OPENAI 免费API2. openai调用最简单的API3.apiKey提取到环境变量 1. OPENAI 免费API 需要科学上网&#xff0c;可以调用 gpt-4o-mini 的 api, 使用其它旧的GPT&#xff0c;反而可能需要收费&#xff0c;例如 gpt-3.5-turbo 2. op…...

【DeepSeek论文精读】11. 洞察 DeepSeek-V3:扩展挑战和对 AI 架构硬件的思考

欢迎关注[【AIGC论文精读】](https://blog.csdn.net/youcans/category_12321605.html&#xff09;原创作品 【DeepSeek论文精读】1. 从 DeepSeek LLM 到 DeepSeek R1 【DeepSeek论文精读】7. DeepSeek 的发展历程与关键技术 【DeepSeek论文精读】11. 洞察 DeepSeek-V3&#xff…...

MySQL事务的一些奇奇怪怪知识

Gorm事务有error却不返回会发生什么 Gorm包是大家比较高频使用。正常的用法是&#xff0c;如果有失败返回error&#xff0c;整体rollback&#xff0c;如果不返回error则commit。下面是Transaction的源码&#xff1a; // Transaction start a transaction as a block, return …...

C语言内存函数与数据在内存中的存储

一、c语言内存函数 1、memcpy函数是一个标准库函数&#xff0c;用于内存复制。功能上是用来将一块内存中的内容复制到另一块内存中。用户需要提供目标地址、源地址以及要复制的字节数。例如结构体之间的复制。 memcpy函数的原型是&#xff1a;void* memcpy&#xff08;void* …...

Power BI Desktop运算符和新建列

1.运算符 运算符 含义 加 - 减 * 乘 / 除 ^ 幂 运算符 含义 等于 > 大于 < 小于 > 大于等于 < 小于等于 <> 不等于 运算符 含义 && 与 || 或 not 非 & 字符串连接 in 包含 not in 不包含 2.新建列 …...

windows 安装gdal实现png转tif,以及栅格拼接

windows 安装gdal实现png转tif&#xff0c;以及栅格拼接 一、安装gdal 网上有很多安装gdal的方法&#xff0c;此处通过osgeo4w安装gdal 1.下载osgeo4w 下载地址 https://trac.osgeo.org/osgeo4w/ 2、安装osgeo4w exe文件安装&#xff0c;前面部分很简单&#xff0c;就不再…...

【嵙大o】C++作业合集

​ 参考&#xff1a; C swap&#xff08;交换&#xff09;函数 指针/引用/C自带-CSDN博客 Problem IDTitleCPP指针CPP引用1107 Problem A编写函数&#xff1a;Swap (I) (Append Code)1158 Problem B整型数据的输出格式1163 Problem C时间&#xff1a;24小时制转12小时制1205…...

论信息系统项目的采购管理

论信息系统项目的采购管理 背景一、规划采购管理二、实施采购三、控制采购结语 背景 某市为对扶贫对象实施精确识别、精确帮扶、精确管理&#xff0c;决定由民政部门牵头&#xff0c;建设家庭经济状况分析及市、县&#xff08;区&#xff09;、镇&#xff08;街&#xff09;三级…...

创建型:单例模式

目录 1、核心思想 2、实现方式 2.1 饿汉式 2.2 懒汉式 2.3 枚举&#xff08;Enum&#xff09; 3、关键注意事项 3.1 线程安全 3.2 反射攻击 3.3 序列化与反序列化 3.4 克隆保护 4、适用场景 1、核心思想 目的&#xff1a;确保一个类仅有一个实例 功能&#xff1a;…...

职场方法论总结(4)-如何正确地汇报

一、明确汇报目标 区分类型&#xff1a;是项目进展汇报&#xff1f;数据总结&#xff1f;问题解决方案&#xff1f;还是资源申请&#xff1f;明确目标才能聚焦内容。听众需求&#xff1a; 所有人都希望你用最简短的语言把事情讲清楚&#xff0c;节省时间领导关注结果、风险和资…...

STM32SPI实战-Flash模板

STM32SPI实战-Flash模板 一&#xff0c;常用指令集&#xff08;部分&#xff09;二&#xff0c;组件库GD25QXX API 函数解析1,前提条件2,初始化与识别1, void spi_flash_init(void)2, uint32_t spi_flash_read_id(void) 3,擦除操作1, void spi_flash_sector_erase(uint32_t sec…...

CSS- 4.4 固定定位(fixed) 咖啡售卖官网实例

本系列可作为前端学习系列的笔记&#xff0c;代码的运行环境是在HBuilder中&#xff0c;小编会将代码复制下来&#xff0c;大家复制下来就可以练习了&#xff0c;方便大家学习。 HTML系列文章 已经收录在前端专栏&#xff0c;有需要的宝宝们可以点击前端专栏查看&#xff01; 点…...

【Retinanet】训练自己的数据集

目录 1.下载源码2.配置环境3.数据集准备4.训练自己的数据5.成功训练&#xff01; 1.下载源码 Retinanet代码&#xff1a;代码 下载到你的目录中&#xff0c;进行打开。 2.配置环境 这里就是cudapytorch&#xff0c;没有配置过的可以参考博客&#xff1a; 深度学习环境的搭建…...

微软将于 8 月 11 日关闭 Bing Search API 服务

微软宣布将于 2025 年 8 月 11 日正式关闭 Bing Search API 服务。届时&#xff0c;所有使用 Bing Search API 的实例将完全停用&#xff0c;同时不再接受新用户注册。 此次停用决定主要影响 Bing Search F1 及 S1 到 S9 资源的用户&#xff0c;以及 Custom Search F0 与 S1 到…...

探索 Python 的利器:help()、dir() 与 AI 工具的结合应用

引言 在编程世界中,Python 以其简洁的语法、强大的功能和丰富的库生态系统成为众多开发者的首选语言。无论是初学者还是资深工程师,在学习新模块、调试代码或探索未知功能时,常常需要有效的工具来帮助理解和解决问题。Python 提供了内置的 help() 和 dir() 函数,让开发者能…...

MySQL查询优化器底层原理解析:从逻辑优化到物理优化

MySQL查询优化器底层原理解析&#xff1a;从逻辑优化到物理优化 引言 在数据库系统中&#xff0c;SQL语句的执行效率直接影响着整个应用的性能表现。一条普通的SQL执行前会经历五个关键阶段&#xff1a;SQL输入、语法分析、语义检查、SQL优化、SQL执行。其中&#xff0c;SQL优…...

UI架构的历史与基础入门

本笔记的目的是通过一系列连贯的例子来探讨“事物-模型-视图-编辑器”这一隐喻。 这些例子都来自我的规划系统&#xff08;planning system&#xff09;&#xff0c;用于解释上述四个概念。所有例子都已实现&#xff0c;但并未在本文描述的清晰类结构中实现。 这些隐喻对应于《…...

(三)MMA(KeyCloak身份服务器/OutBox Pattern)

文章目录 项目地址一、KeyCloak二、OutBox Pattern2.1 配置Common模块的OutBox1. OutboxMessage2. 数据库配置OutboxMessageConfiguration3. 创建Save前的EF拦截器4. 创建Quartz后台任务5. 配置后台任务6. 注册服务2.2 创建OutBox的消费者项目地址 教程作者:教程地址:代码仓库…...

【通用智能体】Playwright:跨浏览器自动化工具

Playwright&#xff1a;跨浏览器自动化工具 一、Playwright 是什么&#xff1f;二、应用场景及案例场景 1&#xff1a;端到端&#xff08;E2E&#xff09;测试场景 2&#xff1a;UI 自动化&#xff08;表单批量提交&#xff09;场景 3&#xff1a;页面截图与 PDF 生成场景 4&am…...

单片机设计_停车场车位管理系统(AT89C52、LCD1602)

想要更多项目私wo!!! 一、电路设计 此电路由AT89C52单片机和LCD1602液晶显示模块等器件组成。 二、运行结果 三、部分代码 #include <reg52.h> //调用单片机头文件 #define uchar unsigned char //无符号字符型 宏定义 变量范围0~255 #define uint unsigned…...

【android bluetooth 协议分析 01】【HCI 层介绍 5】【SetEventMask命令介绍】

1. HCI_Set_Event_Mask 命令作用 项目内容命令名HCI_Set_Event_MaskOCF0x0001作用主机通过设置 Event Mask 告诉控制器&#xff1a;我只对某些事件感兴趣&#xff0c;屏蔽其他事件&#xff0c;以减少中断。事件来源事件是 HCI 与主机之间通信的反馈机制&#xff0c;控制器通过…...

python打卡day29

类的装饰器 知识点回顾 类的装饰器装饰器思想的进一步理解&#xff1a;外部修改、动态类方法的定义&#xff1a;内部定义和外部定义 回顾一下&#xff0c;函数的装饰器是 &#xff1a;接收一个函数&#xff0c;返回一个修改后的函数。类也有修饰器&#xff0c;类装饰器本质上确…...

【数据结构】树状数组

树状数组 假设一个数可以 x x x可以被二进制分解成 x 2 i 1 2 i 2 . . . 2 i m x 2^{i_1} 2^{i_2} ... 2^{i_m} x2i1​2i2​...2im​&#xff0c;不妨设 i 1 > i 2 > . . . > i m i_1 > i_2 > ... > i_m i1​>i2​>...>im​&#xff0c;进…...

Java虚拟机 - JVM与Java体系结构

Java虚拟机 JVM与Java体系结构为什么要学习JVMJava与JVM简介Java 语言的核心特性JVM&#xff1a;Java 生态的基石JVM的架构模型基于栈的指令集架构&#xff08;Stack-Based&#xff09;基于寄存器的指令集架构&#xff08;Register-Based&#xff09;JVM生命周期 总结 JVM与Jav…...

翻译:20250518

翻译题 文章目录 翻译题一带一路中国结 一带一路 The “One Belt and One Road” Initiative aims to achieve win-win and shared development. China remains unchanged in its commitment to foster partnerships. China pursues an independent foreign policy of peace, …...

SparkSQL基本操作

以下是 Spark SQL 的基本操作总结&#xff0c;涵盖数据读取、转换、查询、写入等核心功能&#xff1a; 一、初始化 SparkSession scala import org.apache.spark.sql.SparkSession val spark SparkSession.builder() .appName("Spark SQL Demo") .master("…...

Ansible模块——文件内容修改

修改文件单行内容 ansible.builtin.lineinfile 可以按行修改文件内容&#xff0c;一次修改一行&#xff0c;支持正则表达式。 选项名 类型 默认值 描述 attributesstrnull 设置目标文件的 Linux 文件系统属性&#xff08;attribute bits&#xff09;&#xff0c;作用类似于…...

基于单片机路灯自动控制仪仿真设计

标题:基于单片机路灯自动控制仪仿真设计 内容:1.摘要 本设计旨在解决传统路灯控制方式效率低、能耗大的问题&#xff0c;开展了基于单片机的路灯自动控制仪仿真设计。采用单片机作为核心控制单元&#xff0c;结合光照传感器、时钟模块等硬件&#xff0c;运用相关软件进行编程和…...

Spring Web MVC————入门(3)

今天我们来一个大练习&#xff0c;我们要实现一个登录界面&#xff0c;登录进去了先获取到登录人信息&#xff0c;可以选择计算器和留言板两个功能&#xff0c;另外我们是学后端的&#xff0c;对于前端我们会些基础的就行了&#xff0c;知道ajax怎么用&#xff0c;知道怎么关联…...

拓展运算符与数组解构赋值的区别

拓展运算符与数组解构赋值是ES6中用于处理数组的两种不同的特性&#xff0c;它们有以下区别&#xff1a; 概念与作用 • 拓展运算符&#xff1a;主要用于将数组展开成一系列独立的元素&#xff0c;或者将多个数组合并为一个数组&#xff0c;以及在函数调用时将数组作为可变参…...

【Linux】第二十章 管理基本存储

目录 1. 对 Linux 磁盘进行分区时有哪两种方案&#xff1f;分别加以详细说明。 2. 简单说下创建MBR磁盘分区涉及哪几个步骤&#xff1f; 3. 创建GPT分区与创建MBR分区有什么不同&#xff1f; 4. 在创建分区时就会在分区上创建文件系统吗&#xff1f; 5. 如何持久挂载文件系…...

DeepSeek本地部署全攻略:从零搭建到Web可视化及数据训练

目录 1. 环境准备与硬件要求2. 安装Ollama框架3. 部署DeepSeek模型4. Web可视化配置5. 数据投喂与模型训练6. 进阶技巧与常见问题1. 环境准备与硬件要求 硬件配置建议 基础配置:16GB内存 + RTX 3060显卡(流畅运行7B参数模型)进阶配置:32GB内存 + RTX 4090显卡(支持14B模型…...

JavaScript性能优化实战(12):大型应用性能优化实战案例

在前面的系列文章中,我们探讨了各种JavaScript性能优化技术和策略。本篇将聚焦于实际的大型应用场景,通过真实案例展示如何综合运用这些技术,解决复杂应用中的性能挑战。 目录 电商平台首屏加载优化全流程复杂数据可视化应用性能优化案例在线协作工具的实时响应优化移动端W…...

前缀和——中心数组下标

此题我们不应局限于前缀和的模板&#xff0c;因为该中心下标把数组分为两个部分且每个部分都要求和&#xff0c;我们就一个再创建一个”后缀和” 定义两个数组f&#xff0c;g。f[i]表示[0,i-1]所有元素的和 f[i]f[i-1]nums[i-1];g[i]表示[i1,n-1]的和 g[i]g[i1]nums[i1];因为依…...

Java——创建多线程的四种方式

一、继承Thread 步骤 1.定义一个类继承Thread 2.重写run方法&#xff0c;在方法中设置线程任务&#xff08;此线程具体执行的代码&#xff09; 3.创建自定义线程类对象 4.调用Thread中的start方法&#xff0c;开启线程&#xff0c;jvm自动调用run方法 常用方法 void sta…...

广域网学习

PPPoE技术&#xff08;拨号上网&#xff09; PPPoE &#xff08; PPP over Ethernet &#xff0c;以太网承载 PPP 协议&#xff09;是一种把 PPP 帧封装到以太网帧中的链路层协议。 PPPoE 可以使以太网网络中的多台主机连接到远端的宽带接入服务器。 应用场景 PPPoE 组网结构采…...

inverse-design-of-grating-coupler-3d

一、设计和优化3D光栅耦合器 1.1 代码讲解 通过预定义的环形间距参数(distances数组),在FDTD中生成椭圆光栅结构,并通过用户交互确认几何正确性后,可进一步执行参数扫描优化。 # os:用于操作系统相关功能(如文件路径操作) import os import sys# lumapi:Lumerical 的…...