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

多线程编程的简单案例——单例模式[多线程编程篇(3)]

目录

前言

1.wati() 和 notify()

wait() 和 notify() 的产生原因

如何使用wait()和notify()?

 案例一:单例模式 

 饿汉式写法:

 懒汉式写法 

对于它的优化

 再次优化

结尾 

前言

如何简单的去使用jconsloe 查看线程 (多线程编程篇1)_eclipse查看线程-CSDN博客

浅谈Thread类及常见方法与线程的状态(多线程编程篇2)_thread.join() 和thread.get()-CSDN博客

这是系列的第三篇博客,这篇博客笔者想结合自己的学习经历,分享几个多线程编程的简单案例,帮助读者们更快的理解多线程编程,也非常感激能耐心阅读本系列博客的读者们!

本篇博客的内容如下,您可以通过目录导航直接传送过去

1.介绍wait()和notify()这两个方法

2.介绍单例模式

废话不多说,让我们开始吧,希望我们在知识的道路上越走越远!

博客中出现的参考图都是笔者手画或者截图的的

代码示例也是笔者手敲的!

影响虽小,但请勿抄袭

1.wati() 和 notify()

wait() 和 notify() 的产生原因

在多线程编程中,多个线程同时读写共享资源非常常见。假设两个线程要交替操作一个数据,比如:

  • 线程A:负责生产数据;

  • 线程B:负责消费数据。

如果没有协调机制,线程A和线程B的执行顺序完全由CPU调度,极有可能出现这种情况:

  • 线程B执行时,发现A还没生产好;

  • 线程A刚生产好,B却还没来消费。

这样会出现资源使用错误,甚至死循环。

所以,Java提供了 wait()notify(),解决线程之间通信的问题,帮助程序做到:

 一个线程在条件不满足时,自动等待。
 另一个线程操作完后,主动唤醒等待的线程。

这种机制,叫做等待-通知机制"。

具体来说:

wait()方法:让指定的程序进入阻塞状态

wait 结束等待的条件 :
1.其他线程调用该对象的 notify 方法 .
2.wait 等待时间超时 (wait 方法提供一个带有 timeout 参数的版本 , 来指定等待时间 ).
3.其他线程调用该等待线程的 interrupted 方法 , 导致 wait 抛出 InterruptedException 异常 .

notify()方法:唤醒对应的处在阻塞状态的线程.

举个生活中的例子:

假设你去银行取号排队:

  • 你取号后坐在椅子上等待(相当于调用 wait() 进入等待状态)。

  • 银行的叫号系统喊你的号码时,你再去窗口办理业务(相当于 notify() 唤醒你)。

如果没有这个等待机制,你可能得不停地站在窗口问“轮到我了吗?什么时候才能到我啊?前面的人能不能tm快点啊!”(浪费CPU资源)

有了 wait()notify(),就能让线程“高效地等待”而不是死循环轮询

如何使用wait()和notify()?

OK了解了他们的概念和作用,接下来,笔者将介绍如何使用wait()和notify()

首先,读者们需要了解一些前置知识

第一:根据源码文档,wait() 方法在调用时,必须处理 InterruptedException
因此使用时要么用 try-catch 捕获,要么在方法上声明 throws,否则代码无法通过编译。

第二:wait() 和 notify() 方法并不是定义在 Thread 类中,而是属于 Object 类的方法。
所以在实际使用中,我们通常需要先创建一个 Object 对象,通过这个对象来调用 wait()和 notify(),并且配合 synchronized 关键字一起使用,确保线程安全。

请看一组示例代码:

public class Demo
{public static void main(String[] args) {Object  ob = new Object();Object  lock = new Object();Thread thread1 = new Thread(() ->{synchronized (ob){System.out.println("wait 之前");try {ob.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("进入了");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("wait 之后");});
wait 做的事情:
使当前执行代码的线程进行等待. (把线程放到等待队列中)
释放当前的锁
满足一定条件时被唤醒, 重新尝试获取这个锁.Thread thread2 = new Thread(()->{try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (ob){System.out.println("通知了");ob.notify();}});thread1.start();thread2.start();}
}

在使用 wait()notify() 这两个方法时,有一个非常重要的前提条件:

调用它们时,必须先持有调用对像的锁,而且必须时同一个对像,否则会抛出异常

我们一定要保证,哪个对像调用了wati(),哪个对像就要调用notify(),或者也要设置好阻塞时间. 

synchronized (ob) {ob.wait();  //  正确,线程1的锁对象是 ob
}synchronized (lock) {ob.notify();  //  错误,线程2的锁对象是 lock,调用 notify 却针对 ob
}
错误写法
synchronized (ob) {ob.wait();  //  正确,线程1的锁对象是 ob
}synchronized (ob) {ob.notify();  
正确写法


 案例一:单例模式 

 单例模式是一种设计模式

它保证了一个类在内存中永远只会有一个对象实例.并且提供全局访问点。

举个例子:

假设你要开发一个系统中的配置文件读取器,配置文件只需要加载一次,所有模块都要读取相同的配置信息。如果每次调用都重新 new 一个对象,不仅浪费内存,而且可能导致配置不一致。
通过单例模式,你可以保证这个读取器在整个程序运行期间只创建一次,并且全局唯一! 

又或者 比如 JDBC 中的 DataSource 实例就只需要一个!!!

 单例模式也有两种写法 : 

1.懒汉式: 只要在需要被实例化的时候,才会被实例化.

2.饿汉式:顾名思义,在类内部创建唯一实例,并且用 private static final 修饰,保证类一旦被加载了,就开始实例化了

 饿汉式写法:

public class Singleton {// 饿汉单例,类一旦被加载,就开始实例化了// 1️⃣ 在类内部创建唯一实例,并用 `private static final` 修饰private static final Singleton demo = new Singleton();// 2️⃣ 私有构造方法,防止外部创建实例// 静态代码块private Singleton() {System.out.println("Singleton 实例被创建");}// 3️⃣ 提供公共方法获取实例public static Singleton getInstance() {return demo;}
}

在饿汉式单例中,我们会直接在类内部创建好对象实例,当类加载进内存时,实例就已经完成了初始化。

这是因为我们使用了 static 关键字来修饰这个实例,static 属于类本身,随着类的加载而初始化。
所以,只要 JVM 加载这个类,单例对象就会被创建,并且保证全局只有一个。

在 Java 中,static 修饰的属性或方法属于类本身,而不是某个具体对象。
类被加载到内存时,所有 static 修饰的成员(属性、方法、代码块)会随类一起初始化,而且只会初始化一次。

也就是说:

  • 类加载时,static 属性会被分配内存并初始化。

  • static 方法属于类本身,不依赖对象,可以通过类名.方法名()调用。

我们简单测试一下:

class  MyTest
{public static void main(String[] args) {Singleton s1 =  Singleton.getInstance();}
}

调用  Singleton.getInstance()的时候,类被加载,demo被初始化,并且  Singleton() 构造方法被执行,打印"Singleton 实例被创建".

 
懒汉式写法 

类加载的时候不创建实例 . 第一次使用的时候才创建实例 . 我们依据这个思路,写出来懒汉式单例
public class SingletonLazy {// 1️⃣ 声明一个静态变量用来存储实例private static  SingletonLazy instance;// 2️⃣ 私有构造方法,防止外部创建实例private SingletonLazy() {System.out.println("SingletonLazy 构造方法执行:对象创建成功!");}// 3️⃣ 提供公共的静态方法来获取实例,第一次调用时实例化public static SingletonLazy getInstance() {instance = new SingletonLazy();return instance;}

为了测试懒汉和饿汉的不同,我们再写两个辅助的静态方法测试:

public class SingletonLazy {// 1️⃣ 声明一个静态变量用来存储实例private static  SingletonLazy instance;// 2️⃣ 私有构造方法,防止外部创建实例private SingletonLazy() {System.out.println("SingletonLazy 构造方法执行:对象创建成功!");}// 3️⃣ 提供公共的静态方法来获取实例,第一次调用时实例化public static SingletonLazy getInstance() {instance = new SingletonLazy();return instance;}static {System.out.println("SingletonLazy 类已加载!");}public static void printf() {System.out.println("调用了静态方法 printf()");}}

测试一下:

class Test {public static void main(String[] args) {// 不调用 getInstance 只调用静态方法SingletonLazy.printf();  // 会触发类加载,但不会创建对象!System.out.println("---------------");// 真正调用 getInstance,才会创建对象SingletonLazy s1 = SingletonLazy.getInstance();SingletonLazy s2 = SingletonLazy.getInstance();}
}

结果如下:

调用静态方法后,类会被加载,但此时并不会执行构造方法,也就是说对象还没有被创建。只有当调用 getInstance()  方法时,程序才会真正实例化对象,执行构造方法,完成对象的创建!

我们还可以做一点优化,我们都知道这是单例模式, 只允许有一个对象实例,那么,只有第一次访问时才需要被创建,后续就不用再次创建了,因此可以写成:

public class SingletonLazy {// 1️⃣ 声明一个静态变量用来存储实例private static volatile SingletonLazy instance;// 2️⃣ 私有构造方法,防止外部创建实例private SingletonLazy() {System.out.println("SingletonLazy 构造方法执行:对象创建成功!");}// 3️⃣ 提供公共的静态方法来获取实例,第一次调用时实例化public static SingletonLazy getInstance() {if(instance == null){instance = new SingletonLazy();           }return instance;}
}

 如果在单线程编程下,这样就挑不出毛病了!

对于它的优化

但是,假设在多线程环境下,有复数个线程同时调用  getInstance() ,那么就会创建出多个实例

举一个具体的例子

一旦程序进入多线程环境,比如存在A、B、C 三个线程,它们几乎在同一时刻调用 getInstance()方法

在这一瞬间,instance 的确是 null,三个线程会同时通过 if 判断,然后同时执行 new SingletonLazy(),最终结果就是:

创建了多个实例,破坏了单例模式!!!

因此,我们希望判断是否为空,以及创建实例,这两个动作"原子化"——即不会也不能被打断

怎么办?聪明的你肯定想到了,加锁!

    public static SingletonLazy getInstance() {     synchronized (SingletonLazy.class) {if (instance == null) {instance = new SingletonLazy();}}return instance;}

加完锁以后,刚刚的情况就会变为:

1.假设程序运行在多线程环境下,A、B、C 三个线程几乎在同一时间,调用了 getInstance() 方法。

2.在这一瞬间,instance 的确是 null,于是三个人一起冲进来,准备创建对象。但是!因为这里加了 synchronized,所以三个线程必须抢锁,只有一个幸运儿能抢到,比如A线程。

3.然后A线程释放锁,B、C线程后面排队进来,发现 instance 已经不再是 null,所以它们就啥也不干,直接返回已有的实例。

4.这样一来,就保证了全局唯一实例,不会被多线程同时创建多个,单例模式真正实现了!

 再次优化

不过啊,虽然上面这种“方法加锁”确实解决了多线程下的安全问题——只要一个线程进来了,其他线程就乖乖排队,等着用同一个实例,表面上看没毛病。

但是!问题又来了:

每次调用 getInstance(),都要加锁。
不管 instance 有没有被创建,线程都得卡着 synchronized 排队。

想一想——如果我已经拿到实例了,后面无数次调用其实都只是想用一下这个对象,根本不需要再创建,可还是得老老实实抢锁,这效率能不低吗? 毕竟,加锁的开销也不小了.

所以,聪明的程序员又想了个办法,叫:

双重检查锁(Double-Check Locking),简称 DCL。

核心思路就一句话:

先检查,不满足再加锁,锁住后再检查,确认安全后再创建。

也就是说,外面先检查一次,里面再检查一次,这样只有在 instance 真正等于 null 的时候,才会走到创建对象的逻辑,其他时候,直接跳过锁,快速返回。

public class SingletonLazy {// 加上 volatile,防止指令重排序private static volatile SingletonLazy instance;private SingletonLazy() {System.out.println("SingletonLazy 构造方法执行:对象创建成功!");}public static SingletonLazy getInstance() {if (instance == null) {  // 第一次检查synchronized (SingletonLazy.class) {if (instance == null) {  // 第二次检查instance = new SingletonLazy();}}}return instance;}
}

而且还有个小细节,volatile 关键字也别忘了加上!

因为 Java 内存模型中,new 操作可能会被“重排序”

那么,还是刚刚ABC三线程竞争的例子:

1.

A、B、C 三个线程同时调用 getInstance(),一起执行第一次 if (instance == null)

2. 假设 instance 真的为 null,于是三个线程都准备往下走。

3.

A、B、C 到达 synchronized 这里,开始抢锁。假设A赢了,进入同步代码块。

A 再次执行第二次 if (instance == null),发现确实为空,于是创建 new SingletonLazy()
A 创建完成后,释放锁。

4.

B、C 排队进来,再次检查 if (instance == null),发现已经不为空了,直接跳过创建,返回已存在的实例。 

这样对比普通加锁的好处是,实例化以后,先判断一下是否是空,而不是多个线程直接去竞争锁导致资源浪费

总结一句话:
DCL的好处就是,实例化之后,线程们先看一眼:
"对象在不在?"
在,就立刻用!
不在,才排队抢锁。

相比“每次都抢锁”的方式,DCL大幅减少了资源浪费,尤其适合多线程访问频繁的场景。

完整代码:

public class SingletonLazy {// 1️⃣ 声明一个静态变量用来存储实例private static volatile SingletonLazy instance;// 2️⃣ 私有构造方法,防止外部创建实例private SingletonLazy() {System.out.println("SingletonLazy 构造方法执行:对象创建成功!");}// 3️⃣ 提供公共的静态方法来获取实例,第一次调用时实例化public static SingletonLazy getInstance() {if(instance == null){synchronized (SingletonLazy.class) {if (instance == null) {instance = new SingletonLazy();}}}return instance;}
// 外层 if 的作用:
// 避免已经实例化对象的情况下,仍然加锁。因为加锁是一种消耗性能的操作,
// 所以外层先判断,能直接返回就直接返回,提高效率。// 内层 if 的作用:
// 防止多个线程在 instance == null 的情况下,同时进入同步代码块,
// 抢锁后,重复创建实例。内层 if 可以保证只有第一个抢到锁的线程会创建实例。// 假设 instance 初始为 null,两个线程 A 和 B 几乎同时调用 getInstance():
// 【第一阶段:外层 if 判断(无锁)】
// - 线程A发现 instance == null,进入同步块等待抢锁。
// - 线程B也发现 instance == null,也准备进入同步块等待抢锁。// 【第二阶段:尝试获取锁】
// - 线程A抢到 synchronized(SingletonLazy.class) 的锁,进入同步块,开始执行内层代码。
// - 线程B未抢到锁,必须等待线程A释放锁,挂起等待。// 【第三阶段:内层 if 判断】
// - 线程A在内层再次检查 instance 是否为 null,
//   如果确实是 null,就创建 SingletonLazy 实例。
// - 线程A释放锁,线程B接着抢到锁。// 【第四阶段:线程B再次检查】
// - 线程B进入同步块,内层 if 判断时,发现 instance 已经不是 null,
//   所以不会再创建新对象,直接返回已存在的实例。// 【总结】
// 这样写的双重检查机制,既保证了线程安全,
// 又避免每次都去加锁,提升了性能!// 辅助方法,观察类是否加载static {System.out.println("SingletonLazy 类已加载!");}public static void printf() {System.out.println("调用了静态方法 printf()");}
}class Test {public static void main(String[] args) {// 不调用 getInstance 只调用静态方法SingletonLazy.printf();  // 会触发类加载,但不会创建对象!System.out.println("---------------");// 真正调用 getInstance,才会创建对象SingletonLazy s1 = SingletonLazy.getInstance();SingletonLazy s2 = SingletonLazy.getInstance();}
}

结尾 

写到这里的时候,大约花费了笔者120分钟,写了8145个字

本来笔者想接着介绍阻塞队列的,看来只能留到下次了!

笔者的风格是每一步都会写的很详细,因为笔者觉得自己天赋不佳,需要在学会的时候记录的越详细越好,方便读者查阅和调用

希望笔者如此之高质量的博客能帮助到你我他!

相关文章:

多线程编程的简单案例——单例模式[多线程编程篇(3)]

目录 前言 1.wati() 和 notify() wait() 和 notify() 的产生原因 如何使用wait()和notify()? 案例一:单例模式 饿汉式写法: 懒汉式写法 对于它的优化 再次优化 结尾 前言 如何简单的去使用jconsloe 查看线程 (多线程编程篇1)_eclipse查看线程-CSDN博客 浅谈Thread类…...

万物互联时代,AWS IoT Core如何构建企业级物联网中枢平台?

在智能制造、智慧城市、车联网等场景爆发的今天,全球物联网设备数量已突破150亿台。企业如何高效管理海量设备并挖掘数据价值?AWS IoT Core作为亚马逊云科技推出的全托管物联网平台,正在为数千家企业提供设备连接、数据采集、实时分析的一站式…...

论文阅读:2023 arxiv Safe RLHF: Safe Reinforcement Learning from Human Feedback

总目录 大模型安全相关研究:https://blog.csdn.net/WhiffeYF/article/details/142132328 Safe RLHF: Safe Reinforcement Learning from Human Feedback https://arxiv.org/pdf/2310.12773 https://github.com/PKU-Alignment/safe-rlhf 速览 研究动机&#xff…...

链表相关算法题

小细节 初始化问题 我们这样子new一个ListNode 它里面的默认值是0,所以我们不能这样 如果我们为空,我们要返回null 节点结束条件判断(多创建节点问题) 参考示例3217 解析: 我的答案是多了一个无用节点 这是因为我每…...

招商信诺原点安全:一体化数据安全管理解决方案荣获“鑫智奖”!

近日,“鑫智奖 2025第七届金融数据智能优秀解决方案评选”榜单发布,原点安全申报的《招商信诺:数据安全一体化管理解决方案》荣获「信息安全创新优秀解决方案」。 “鑫智奖第七届金融数据智能优秀解决方案评选”活动由金科创新社主办&#x…...

实战篇|多总线网关搭建与量产验证(5000 字深度指南)

引言 1. 环境准备与硬件选型 1.1 项目需求分析 1.2 SoC 与开发板选型 1.3 物理接口与 PCB 设计 1.4 电源与供电保护 2. 软件架构与协议栈移植 2.1 分层架构详解 2.2 协议栈移植步骤 2.3 高可用驱动设计 2.4 映射逻辑与 API 定义 3. 开发流程与实践 3.1 敏捷迭代与里程碑 3.2 核…...

Jenkins 简易使用记录

一、Jenkins 核心功能与适用场景 核心功能: 持续集成(CI):自动构建代码、运行单元测试。持续交付(CD):自动化部署到测试/生产环境。任务调度:定时执行任务(如备份、清理&…...

第十四节:实战场景-何实现全局状态管理?

React.createElement调用示例 Babel插件对JSX的转换逻辑 React 全局状态管理实战与 JSX 转换原理深度解析 一、React 全局状态管理实现方案 1. Context API useReducer 方案(轻量级首选) // 创建全局 Context 对象 const GlobalContext createConte…...

启动vite项目报Unexpected “\x88“ in JSON

启动vite项目报Unexpected “\x88” in JSON 通常是文件被防火墙加密需要寻找运维解决 重启重装npm install...

Jenkins 多分支管道

如果您正在寻找一个基于拉取请求或分支的自动化 Jenkins 持续集成和交付 (CI/CD) 流水线,本指南将帮助您全面了解如何使用 Jenkins 多分支流水线实现它。 Jenkins 的多分支流水线是设计 CI/CD 工作流的最佳方式之一,因为它完全基于 git(源代…...

PHP腾讯云人脸核身获取NONCE ticket

参考腾讯云官方文档: 人脸核身 获取 NONCE ticket_腾讯云 前提条件,已经成功获取了access token。 获取参考文档: PHP腾讯云人脸核身获取Access Token-CSDN博客 public function getTxFaceNonceTicket($uid) {$access_token file_get_c…...

云计算(Cloud Computing)概述——从AWS开始

李升伟 编译 无需正式介绍亚马逊网络服务(Amazon Web Services,简称AWS)。作为行业领先的云服务提供商,AWS为全球开发者提供了超过170项随时可用的服务。 例如,Adobe能够独立于IT团队开发和更新软件。通过AWS的服务&…...

51单片机实验五:A/D和D/A转换

一、实验环境与实验器材 环境:Keli,STC-ISP烧写软件,Proteus. 器材:TX-1C单片机(STC89C52RC)、电脑。 二、 实验内容及实验步骤 1.A/D转换 概念:模数转换是将连续的模拟信号转换为离散的数字信…...

重构未来智能:Anthropic 解码Agent设计哲学三重奏

第一章 智能体进化论:从工具到自主体的认知跃迁 1.1 LLM应用范式演进图谱 阶段技术形态应用特征代表场景初级阶段单功能模型硬编码规则执行文本摘要/分类进阶阶段工作流编排多模型协同调度跨语言翻译流水线高级阶段自主智能体动态决策交互编程调试/客服对话 1.1.…...

MCP协议在纳米材料领域的深度应用:从跨尺度协同到智能研发范式重构

MCP协议在纳米材料领域的深度应用:从跨尺度协同到智能研发范式重构 文章目录 MCP协议在纳米材料领域的深度应用:从跨尺度协同到智能研发范式重构一、MCP协议的技术演进与纳米材料研究的适配性分析1.1 MCP协议的核心架构升级1.2 纳米材料研发的核心挑战与…...

.NET Core 服务实现监控可观测性最佳实践

.NET Core 概述 .Net Core 是一个开源的、跨平台的高性能框架,由微软开发并维护,现由 .NET Foundation 提供支持。它用于构建现代化、可扩展的云端和本地应用程序,支持开发 Web 应用、微服务、API、物联网应用以及移动后端服务,是…...

ios精灵脚本辅助软件,有根和无根roothide越狱区别

最新版本的ios按键精灵app 支持到15-16系统,可以在半越狱环境下和无根越狱环境安装,对于很多用户一直不理解有根和无根之间的差别,今天简单介绍下 最高权限和部分权限的区别 1、有根越狱 – 有系统根目录读写权限(通过越狱软件可…...

ChatGPT-o3辅助学术大纲效果如何?

目录 1 引言 2 背景综述 2.1 自动驾驶雷达感知 2.2 生成模型演进:从 GAN 到 Diffusion 3 相关工作 3.1 雷达点云增强与超分辨率 3.2 扩散模型在数据增广中的应用 4 方法论 4.1 问题定义与总览 4.2 数据预处理与雷达→体素表示 4.3 潜在体素扩散网络&…...

PyCharm 2024.3.5 状态栏添加前进后退按钮

操作路径:Appearance & Behavior -> Menu and Toolbars -> Main Toolbar -> Left -> Add… 按钮位置:Main Menu -> Navigate -> OK 最终效果...

【CPP】死锁产生、排查、避免

一、死锁产生 死锁是指两个或多个线程互相等待对方释放资源,导致程序无法继续执行的现象。在多线程编程中,死锁是一种常见且严重的并发问题。死锁产生必须要四个条件同时满足才会发生: 互斥条件:某些资源只能由一个线程占用。占…...

深入理解 Android Handler

一、引言 Handler 在安卓中的地位是不言而喻的,几乎维系着整个安卓程序运行的生命周期,但是这么重要的一个东西,我们真的了解它吗?下面跟随着我的脚步,慢慢揭开Hanler的神秘面纱吧! 本文将介绍Handler 的运…...

Git 进阶之路:高效协作之分支管理

🌈 个人主页:Zfox_ 🔥 系列专栏:Git 企业级应用 目录 一:🔥 分⽀管理 🦋 理解分⽀🦋 创建分⽀🦋 切换分⽀🦋 合并分⽀🦋 删除分⽀🦋 合…...

LeetCode 2364.统计坏数对的数目:反向统计

【LetMeFly】2364.统计坏数对的数目&#xff1a;反向统计 力扣题目链接&#xff1a;https://leetcode.cn/problems/count-number-of-bad-pairs/ 给你一个下标从 0 开始的整数数组 nums 。如果 i < j 且 j - i ! nums[j] - nums[i] &#xff0c;那么我们称 (i, j) 是一个 坏…...

6.Rust+Axum:打造高效 WebSocket 实时通信聊天室

摘要 本文详细介绍 RustAxum 在 WebSocket 实时通信开发中的应用&#xff0c;包括双向通信、状态管理等&#xff0c;实践构建聊天室应用。 一、引言 在当今的 Web 应用开发中&#xff0c;实时通信变得越来越重要。WebSocket 作为一种在单个 TCP 连接上进行全双工通信的协议&…...

【硬件系统架构】冯·诺依曼架构

一、引言 在计算机科学的广袤领域中&#xff0c;冯诺依曼架构犹如一颗璀璨的恒星&#xff0c;照亮了现代计算机发展的道路。从我们日常使用的个人电脑到强大的数据中心服务器&#xff0c;几乎都基于这一架构构建。它的出现是计算机发展史上的一个重要里程碑&#xff0c;深刻地影…...

Android 13 关闭屏幕调节音量大小

一、问题描述 在Android 13系统中,通过修改frameworks/base/core/res/res/values/config.xml配置文件,实现灭屏状态下调节音量的功能。 二、修改内容 diff --git a/frameworks/base/core/res/res/values/config.xml b/frameworks/base/core/res/res/values/config.xml inde…...

[编程基础] Java · 学习手册

&#x1f525; 《Java 工程师修炼之路&#xff1a;从零构建系统化知识体系》 &#x1f525; &#x1f6e0;️ 专栏简介&#xff1a; 这是一个以工业级开发标准打造的 Java 全栈技术专栏&#xff0c;涵盖语言核心、并发编程、JVM 原理、框架源码、架构设计等全维度知识体系。专…...

探索元生代:ComfyUI 工作流与计算机视觉的奇妙邂逅

目录 一、引言 二、蓝耘元生代和 ComfyUI 工作流初印象 &#xff08;一&#xff09;蓝耘元生代平台简介 &#xff08;二&#xff09;ComfyUI 工作流创建是啥玩意儿 三、计算机视觉是个啥 &#xff08;一&#xff09;计算机视觉的基本概念 &#xff08;二&#xff09;计算…...

C++ 迭代器失效详解:如何避免 vector 操作中的陷阱

目录 1. 什么是迭代器失效&#xff1f; 2. 哪些操作会导致迭代器失效&#xff1f; 2.1 vector 的插入操作&#xff08;push_back, insert&#xff09; 示例&#xff1a;push_back 导致迭代器失效 如何避免&#xff1f; 2.2 vector 的删除操作&#xff08;erase, pop_back&…...

【fisco bcos】基于ABI调用智能合约

参考官方文档&#xff1a;https://fisco-bcos-documentation.readthedocs.io/zh-cn/latest/docs/sdk/java_sdk/assemble_transaction.html 先放一下智能合约&#xff1a; &#xff08;就是一个很简单的插入和查找嗯&#xff09; pragma solidity ^0.4.25; pragma experimental…...

【Python学习笔记】Pandas实现Excel质检记录表初审、复核及质检统计

背景&#xff1a; 我有这样一个需要审核的飞书题目表&#xff0c;按日期分成多个sheet&#xff0c;有初审——复核——质检三个环节&#xff0c;这三个环节是不同的同学在作业&#xff0c;并且领到同一个题目的人选是随机的&#xff0c;也就是说&#xff0c;完成一道题的三个人…...

Springboot 自动装配原理是什么?SPI 原理又是什么?

1. Spring Boot 自动装配原理 自动装配是 Spring Boot 简化配置的核心机制&#xff0c;其核心思想是根据类路径中的依赖自动配置 Spring 应用。 关键步骤&#xff1a; 启动注解 SpringBootApplication 该注解组合了 EnableAutoConfiguration&#xff0c;用于激活自动配置。 …...

【英语语法】基本句型

目录 前言一&#xff1a;主谓二&#xff1a;主谓宾三&#xff1a;主系表四&#xff1a;主谓双宾五&#xff1a;主谓宾补 前言 英语基本句型是语法体系的基石&#xff0c;以下是英语五大基本句型。 一&#xff1a;主谓 结构&#xff1a;主语 不及物动词 例句&#xff1a; T…...

扫雷-C语言版

C语言扫雷游戏设计&#xff08;完整版&#xff09; 游戏背景 扫雷是一款经典的益智类单人电脑游戏&#xff0c;最早出现在1960年代&#xff0c;并在1990年代随着Windows操作系统而广为人知。游戏目标是在不触发任何地雷的情况下&#xff0c;揭开所有非地雷的格子。玩家需要根…...

【C++初阶】--- list容器功能模拟实现

1.什么是list容器 在 C 标准模板库&#xff08;STL&#xff09;中&#xff0c;std::list 是一个非常有用的容器&#xff0c;它是双向链表的实现std::list 是一种序列式容器&#xff0c;它允许在序列内的任意位置进行高效的插入和删除操作。与数组和 std::vector 不同&#xff…...

gRPC 介绍及在嵌入式 Linux 下的成功编译及使用详解

gRPC 是一个高性能、开源和通用的 RPC 框架&#xff0c;由 Google 开发。它支持多种编程语言&#xff0c;并且能够运行在不同的环境中&#xff0c;包括嵌入式系统。本文将详细介绍 gRPC&#xff0c;以及如何在嵌入式 Linux 系统下成功编译 gRPC&#xff0c;并结合 protobuf 和 …...

C语言教程(十):C 语言函数详解

一、引言 在 C 语言中&#xff0c;函数是一组执行特定任务的代码块。通过将复杂的程序逻辑划分为多个函数&#xff0c;不仅能提高代码的可读性、可维护性&#xff0c;还便于代码的复用。无论是简单的数学计算&#xff0c;还是复杂的系统操作&#xff0c;函数都发挥着核心作用。…...

力扣刷题-热题100题-第35题(c++、python)

146. LRU 缓存 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/lru-cache/?envTypestudy-plan-v2&envIdtop-100-liked 双向链表哈希表 内置函数 对于c有list可以充当双向链表&#xff0c;unordered_map充当哈希表&#xff1b;python有OrderedDic…...

LeetCode算法题(Go语言实现)_52

题目 给你一个下标从 0 开始的整数数组 costs &#xff0c;其中 costs[i] 是雇佣第 i 位工人的代价。 同时给你两个整数 k 和 candidates 。我们想根据以下规则恰好雇佣 k 位工人&#xff1a; 总共进行 k 轮雇佣&#xff0c;且每一轮恰好雇佣一位工人。 在每一轮雇佣中&#xf…...

基于尚硅谷FreeRTOS视频笔记——13—HAL库和RTOS时钟源问题

RTOS的时钟源就是系统定时器中断&#xff0c;通俗来说就是系统定时器每中断一次&#xff0c;就扫描一次RTOS&#xff0c;查看RTOS中有没有任务的切换。 但是&#xff0c;系统存在一个HAL_Delay()函数&#xff0c;就是使用的系统定时器中断来执行的函数。 当我们在RTOS中&…...

FPGA入门学习Day1——设计一个DDS信号发生器

目录 一、DDS简介 &#xff08;一&#xff09;基本原理 &#xff08;二&#xff09;主要优势 &#xff08;三&#xff09;与传统技术的对比 二、FPGA存储器 &#xff08;一&#xff09;ROM波形存储器 &#xff08;二&#xff09;RAM随机存取存储器 &#xff08;三&…...

JavaScript-立即执行函数(Immediately Invoked Function Expression,IIFE)

立即执行函数&#xff08;Immediately Invoked Function Expression&#xff0c;IIFE&#xff09;是 JavaScript 里一种很独特的函数&#xff0c;它在定义后会马上执行。下面会详细介绍它的语法、用途、优点以及注意事项。 一、语法 立即执行函数一般有两种常见的语法形式&am…...

【Leetcode 每日一题 - 补卡】2537. 统计好子数组的数目

问题背景 给你一个整数数组 n u m s nums nums 和一个整数 k k k&#xff0c;请你返回 n u m s nums nums 中 好 子数组的数目。 一个子数组 a r r arr arr 如果有 至少 k k k 对下标 ( i , j ) (i, j) (i,j) 满足 i < j i < j i<j 且 a r r [ i ] a r r [ …...

【工具-Krillin AI】视频翻译、配音、语音克隆于一体的一站式视频多语言转换工具~

Krillin AI 是全能型音视频本地化与增强解决工具。这款简约而强大的工具&#xff0c;集音视频翻译、配音、语音克隆于一身&#xff0c;支持横竖屏格式输出&#xff0c;确保在所有主流平台&#xff08;哔哩哔哩&#xff0c;小红书&#xff0c;抖音&#xff0c;视频号&#xff0c…...

常用绑定事件方式有哪几种

绑定事件分为3种&#xff1a; 1、内联模式&#xff1a;将函数名直接作为标签属性的属性值&#xff08;注意&#xff1a;这里是带括号的,不带括号不生效&#xff0c;但是在vue中可以加括号也可以不加括号&#xff0c;如果需要穿参数就加括号&#xff0c;不需要传参数可以不加&am…...

数据结构之BFS广度优先算法(腐烂的苹果)

队列这个数据结构在很多场景下都有使用&#xff0c;比如在实现二叉树的层序遍历&#xff0c;floodfill问题(等等未完成)中&#xff0c;都需要借助队列的先进先出特性&#xff0c;下面给出这几个问题的解法 经典的二叉树的层序遍历 算法图示&#xff0c;以下图所示的二叉树为例…...

linux 学习 1.开始学习

准备学习linux记录一下学习内容&#xff0c;只会包含必要的知识&#xff0c;和部分演示 我采用的系统是Ubuntu24.04 初始掌握 学习首先需要掌握如何查看帮助手册 man man # man 加任何命令可以看具体命令的帮助手册 man mkdir进入手册按 d(down)&#xff1a;往下翻半页u(u…...

Flink-01学习 介绍Flink及上手小项目之词频统计

flink简介 官网 概述&#xff1a; 学习Flink具体包括四个关键概念&#xff1a;流数据的持续处理&#xff0c;事件时间&#xff0c;有状态流处理和状态快照。 Apache Flink 是一个开源的流处理框架&#xff0c;旨在处理批处理和实时数据处理&#xff0c;具有高吞吐量和低延迟的…...

【Linux我做主】探秘gcc/g++和动静态库

TOC Linux编译器gcc/g的使用 github地址 有梦想的电信狗 前言 在软件开发的世界中&#xff0c;编译器如同匠人的工具&#xff0c;将人类可读的代码转化为机器执行的指令。 对于Linux开发者而言&#xff0c;gcc和g是构建C/C程序的核心工具链&#xff0c;掌握它们的原理和使…...

工控系统前端设计(pyqt)

题目源自&#xff1a;白月黑羽的项目实战四-[工控系统前端] 代码已上传至gitcode https://gitcode.com/m0_37662818/Industrial_Control_System_Front_End 心得体会&#xff1a;直接用组态软件或者js吧 项目亮点 tablemodel的使用&#xff0c;绑定了表格和数据风机自定义ite…...