JavaEE-经典多线程样例
文章目录
- 单例模式
- 设计模式初步引入
- 为何存在单例模式
- 饿汉式单例模式
- 饿汉式缺陷以及是否线程安全
- 懒汉式单例模式
- 基础懒汉式缺陷以及是否线程安全
- 懒汉式单例模式的改进
- 完整代码(变量volatile)
- 阻塞队列
- 生产者消费者模型
- 生产者消费者模型的案例以及优点
- 请求与响应案例
- 解耦合
- 削峰填谷
- 阻塞队列的内置API
- 阻塞队列的模拟实现
- 关于wait和while的搭配使用
- 模拟实现
单例模式
设计模式初步引入
啥是设计模式?
- 设计模式好⽐象棋中的 “棋谱”. 红⽅当头炮, ⿊⽅⻢来跳. 针对红⽅的⼀些⾛法, ⿊⽅应招的时候有⼀些固定的套路. 按照套路来⾛局势就不会吃亏.软件开发中也有很多常⻅的 “问题场景”. 针对这些问题场景, ⼤佬们总结出了⼀些固定的套路. 按照这个套路来实现代码, 也不会吃亏, 不针对某一种语言, 而是针对某种开发场景
- 设计模式并不是只有
23
种, 因为之前有些大佬写了一本书叫设计模式
,重点讨论了23种, 但事实上存在更多种的设计模式 - 设计模式与框架的区别就是, 设计模式在开发中是
软性要求(不一定遵守)
, 但是框架是硬性要求(一定要遵守)
简单点一句话总结
设计模式是前人根据一些开发场景给出的一些经验之谈, 所以设计模式并不针对某一种语言
为何存在单例模式
- 单例模式能保证某个类在程序中只存在唯⼀⼀份实例, ⽽不会创建出多个实例.
这⼀点在很多场景上都需要. 比如 JDBC 中的 DataSource 实例就只需要⼀个,再
比如如果一个类的创建需要加载的数据量非常的庞大(GB级别), 那我们不希望这
个类频繁的创建销毁(开销很大), 我们可能只是希望创建一次就可以了
饿汉式单例模式
顾名思义, 这种方式实现的单例模式十分"饥渴", 不管使用不使用都会提前new一个对象
流程如下
- 构造方法私有化
- 定义一个静态的类对象用以返回
- 提供一个公开的静态接口来获取唯一的对象
测试代码如下
/*** 下面定义一个类来测试饿汉式单例模式*/
class HungrySingleton{// 提供一个静态的变量用来返回private static HungrySingleton hungrySingleton = new HungrySingleton();// 构造方法私有化(在外部不可以构造对象)private HungrySingleton(){}// 提供一个获取实例的静态公开接口public static HungrySingleton getInstance(){return hungrySingleton;}
}public class DesignPatternTest {public static void main(String[] args) {// 对饿汉式单例的测试HungrySingleton instance1 = HungrySingleton.getInstance();HungrySingleton instance2 = HungrySingleton.getInstance();// 测试两者是不是一个对象System.out.println(instance1 == instance2);}
}
测试结果
很明显, 用这种方式创建的实例都是只有一份的…
饿汉式缺陷以及是否线程安全
首先饿汉式的单例模式缺陷是非常明显的
- 饿汉式不管我们使用这个对象与否, 都会在类加载的时期(因为是静态对象)构建一个这样的对象, 但我们想要达成的效果是, 在我们不需要这种类的实例的时候, 我们不去进行构造对象的操作(变主动为被动)来减少内存等相关资源的开销
但是饿汉式单例一定是线程安全的
- 构建对象的时期是类加载的时候, 后期不同线程对于这个实例的操作也仅仅是涉及到读操作, 不涉及修改操作, 所以当然是线程安全的, 不存在线程安全问题, 但是另一种实现的模式就不一定了
懒汉式单例模式
上面说了饿汉式单例模式的缺陷, 我们尝试使用懒汉式单例的方式去解决这个问题, 也就是仅仅在需要的时候进行new对象的操作
最基础的懒汉单例模式
构造的逻辑
- 构造方法私有化
- 提供一个静态的对象用来返回(暂时不new对象)
- 提供一个公开访问的静态接口来返回唯一的对象
代码测试(最基础的版本)
/*** 下面定义一个类来测试懒汉式单例模式*/
class LazySingleton{// 提供一个静态的变量用来返回private static LazySingleton lazySingleton = null;// 构造方法私有化(不可以在外部new对象)private LazySingleton(){}// 提供一个公开的获取实例的接口public static LazySingleton getInstance(){if(lazySingleton == null){lazySingleton = new LazySingleton();}return lazySingleton;}
}public class DesignPatternTest {public static void main(String[] args) {// 对懒汉式单例的测试LazySingleton instance1 = LazySingleton.getInstance();LazySingleton instance2 = LazySingleton.getInstance();// 测试两者是不是一个对象System.out.println(instance1 == instance2);}
}
基础懒汉式缺陷以及是否线程安全
这个就和上面饿汉有较大的区别了, 虽然解决了在需要的时候进行new对象, 上面的基础版本的懒汉式在单线程的环境下肯定是没问题的, 但是在多线程的环境下就不好说了…看下面的分析
如果在多线程的环境下(我们假设有t1, t2)是下图的执行顺序
很明显这是一种类似串行的执行策略
但是还可能是下图的情况
t1线程判断完毕之后没有来得及进行new对象, t2线程紧接着进行了一次完整的new对象的过程, 此时t1线程又进行了一次new对象的过程, 很明显, 我们上面的情况进行了两次构造对象的过程, 同时拿到的对象也不一致
我们通过Thread.sleep()
的方式进行延迟观察看是否会发生
/*** 下面定义一个类来测试懒汉式单例模式*/
class LazySingleton{// 提供一个静态的变量用来返回private static LazySingleton lazySingleton = null;// 构造方法私有化(不可以在外部new对象)private LazySingleton(){}// 提供一个公开的获取实例的接口public static LazySingleton getInstance() throws InterruptedException {if(lazySingleton == null){Thread.sleep(1000);lazySingleton = new LazySingleton();}return lazySingleton;}
}public class DesignPatternTest {private static LazySingleton instance1 = null;private static LazySingleton instance2 = null;public static void main(String[] args) throws InterruptedException {// 创建两个线程获取实例Thread t1 = new Thread(() -> {try {instance1 = LazySingleton.getInstance();} catch (InterruptedException e) {e.printStackTrace();}});Thread t2 = new Thread(() -> {try {instance2 = LazySingleton.getInstance();} catch (InterruptedException e) {e.printStackTrace();}});// 开启两个线程t1.start();t2.start();// 睡眠等待一下Thread.sleep(2000);System.out.println(instance1 == instance2);}
}
很明显, 这样的懒汉式的代码是线程不安全的, 那要如何进行改进呢???
懒汉式单例模式的改进
之前我们说了, 要想保证线程是安全的, 有几种解决方式, 这里面我们就采取加锁, 因为其实
判断是不是null和对象应该是一个整体的原子性的操作
改进之后的代码
/*** 下面定义一个类来测试懒汉式单例模式(改进版)*/
class LazySingleton{// 提供一个静态的变量用来返回private static LazySingleton lazySingleton = null;// 构造方法私有化(不可以在外部new对象)private LazySingleton(){}// 提供一个公开的获取实例的接口public static LazySingleton getInstance() {// 我们把if判断和new对象通过加锁打包为一个原子性的操作(这里使用类对象锁)synchronized (LazySingleton.class){if(lazySingleton == null){lazySingleton = new LazySingleton();}}return lazySingleton;}
}public class DesignPatternTest {private static LazySingleton instance1 = null;private static LazySingleton instance2 = null;public static void main(String[] args) {// 在多线程中获取实例Thread t1 = new Thread(() -> {instance1 = LazySingleton.getInstance();});Thread t2 = new Thread(() -> {instance2 = LazySingleton.getInstance();});System.out.println(instance1 == instance2);}
}
这时候肯定是一个线程安全的代码了, 但是思考可不可以进一步改进呢???
当我们已经new个一次对象之后, 如果后续的线程想要获取这个对象, 那就仅仅是一个读
操作了, 根本不涉及对对象的修改, 但是我们每次都使用锁这样的机制就会造成阻塞, 也就会导致程序的效率下降, 所以我们对代码进行了下面的修改在外层再加一个if判断
改进的方法如下
// 提供一个公开的获取实例的接口public static LazySingleton getInstance() {// 我们把if判断和new对象通过加锁打包为一个原子性的操作(这里使用类对象锁)if (lazySingleton == null) {synchronized (LazySingleton.class) {if (lazySingleton == null) {lazySingleton = new LazySingleton();}}}return lazySingleton;}
我们的两个if的含义
- 第一个
if
: 判断对象是否创建完毕, 如果创建了, 只是一个读操作 - 第二个
if
: 判断是不是需要new对象
可能初学多线程的时候, 看上述代码觉得很迷惑, 但其实这是因为之前我们写的程序都是单线程的情况, 单线程中执行流只有一个, 两次相同的if判断其实是没有必要的, 但是多线程的条件下, 是多个执行流, 相同的逻辑判断条件也可能产生不同的结果
完整代码(变量volatile)
关于变量是否会产生指令重排序和内存可见性
问题, 我们直接加上volatile
即可
/*** 下面定义一个类来测试懒汉式单例模式(完整改进版)*/
class LazySingleton {// 提供一个静态的变量用来返回private volatile static LazySingleton lazySingleton = null;// 构造方法私有化(不可以在外部new对象)private LazySingleton() {}// 提供一个公开的获取实例的接口public static LazySingleton getInstance() {// 我们把if判断和new对象通过加锁打包为一个原子性的操作(这里使用类对象锁)if (lazySingleton == null) {synchronized (LazySingleton.class) {if (lazySingleton == null) {lazySingleton = new LazySingleton();}}}return lazySingleton;}
}
阻塞队列
生产者消费者模型
关于生产者消费者模型, 其实是生活中抽象出来的一个模型案例, 我们举一个包饺子的例子来简单解释一下
-
在包饺子的过程中, 存在一个擀饺子皮的人, 我们称之为生产者, 擀出来的饺子皮放到一个竹盘上, 这个竹盘相当于一个中间的媒介, 生产者生产的物质在上面与消费者进行交互, 而包饺子的人就是一个消费者, 从中间媒介中取出东西, 也就是消费的过程, 我们的中间的竹盘相当于一个缓冲, 如果包饺子的人包的快的话, 就需要等待做饺子皮的人, 如果做饺子皮的人做的快的话, 当竹盘放不下的时候就需要阻塞等待
-
上面的情景抽象成生产者消费者模型, 擀饺子皮的人是生产者, 竹盖是阻塞队列, 包饺子的人是消费者
生产者消费者模型的案例以及优点
请求与响应案例
生产者消费者模型我们举一个"请求响应的案例"
图中我们也有解释, 越靠上游的消耗的资源越少
假设我们现在出现一个秒杀的请求, 上游可能还可以运行, 但是下游的服务器由于并发量过大就直接崩溃了
所以我们一般会对上面提供服务的逻辑进行改变
添加一个中间的结构(阻塞队列, 或者说消息队列)进行缓冲
在真实的开发场景当中, 阻塞队列甚至会单独的部署为一台服务器, 这种独立的服务器结构叫做消息队列, 可见其重要性
解耦合
生产者消费者模型的一个重要的优点就是让消费者和生产者解耦合
- 根据上面的模型分析, 不管是生产者还是消费者都是面向阻塞队列来进行任务的执行的, 所以就降低了两者之间的耦合度, 将来想要修改这个模型的工作内容, 也只需要面向阻塞队列操作更改(其实相当于接口), 如果没有这种机制的话, 我们想要更改一个操作逻辑, 就需要同时修改消费者与生产者的代码结构…, 我们先前学习的接口其实就是一种解耦合的策略, 其核心就是减少耦合度, 便于对代码结构进行调整
削峰填谷
刚才我们的那个模型就说了, 如果消息请求量非常大的时候, 如果没有消息队列的存在, 就会对下游的服务器产生较大的影响, 甚至会导致服务器崩溃
下图是正常情况下消息队列的工作示意图, 添加的任务加入消息队列, 然后下游的服务器以一个相对稳定的效率从队列中取出来任务进行处理
下图是当任务量激增的时候, 虽然任务量激增, 但是依旧进入消息队列进行等待处理, 此时下游的服务器对任务的处理的效率基本不变, 所以可以保证处理的稳定性, 不至于让下游服务器崩溃, 因为这个消息一般都是一阵一阵的激增, 所以等到下一轮消息量减少的时候, 对先前消息队列的数据进行清理即可…
阻塞队列的内置API
下图是我们相关的阻塞队列的内置API继承逻辑
关于构造方法
ArrayBlockingQueue
: 必须指定大小
LinkedBlockQueue
: 可以指定也可以不指定
关于offer
和poll
与put
和take
的区别
首先是offer
和poll
这两个方法也可以使阻塞队列产生阻塞的效果, 但是我们可以指定一个最大的等待时间
我们使用下面的代码测试
/*** 关于阻塞队列的相关测试*/
public class ThreadTest {public static void main(String[] args) {// 生成一个阻塞队列(指定队列的大小为100)BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<Integer>(100);// 创建两个线程测试Thread producer = new Thread(() -> {for(int i = 0; i < 1000; i++){try {blockingQueue.offer(i, 10L, TimeUnit.SECONDS);System.out.println("生产了元素: " + i);} catch (InterruptedException e) {e.printStackTrace();}}});// 消费者线程Thread consumer = new Thread(() -> {while(true){try {// 进行休眠Thread.sleep(1000 * 1);int elem = blockingQueue.take();System.out.println("消费了元素: " + elem);} catch (InterruptedException e) {e.printStackTrace();}}});producer.start();consumer.start();}
}
分析下这个程序的执行的逻辑
- 在程序启动的很短的时间内, 由于阻塞队列的容量还有空余, 所以会大量的生产元素直到阻塞队列满了, 因为消费者线程是每一秒钟消耗一个元素, 所以存在等待时间, 我们上述代码设置的最大的等待时间是
10s
, 所以根本来不及等待到最大的时间点就可以进行取出元素…
put
和take
方法
- 这组方法和上组方法的区别就是, 这个方法是当队列满或者队列空, 我们进行无限期的阻塞…, 直到队列中的元素不为空或者不为满就可以进行操作
/*** 关于阻塞队列的相关测试*/
public class ThreadTest {public static void main(String[] args) {// 生成一个阻塞队列(指定队列的大小为100)BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<Integer>(100);// 创建两个线程测试Thread producer = new Thread(() -> {for(int i = 0; i < 1000; i++){try {blockingQueue.put(i);System.out.println("生产了元素: " + i);} catch (InterruptedException e) {e.printStackTrace();}}});// 消费者线程Thread consumer = new Thread(() -> {while(true){try {// 进行休眠Thread.sleep(1000 * 1);int elem = blockingQueue.take();System.out.println("消费了元素: " + elem);} catch (InterruptedException e) {e.printStackTrace();}}});producer.start();consumer.start();}
}
最后的执行结果如下
在短时间之内进行大量的生产之后开始隔一秒拿出一个元素, 生产一个元素
阻塞队列的模拟实现
关于wait和while的搭配使用
上面是我们的JDK帮助文档
对wait
使用的建议(其实就是源码), 我们官方文档中提倡wait
的使用建议和while
循环搭配, 而不是和if搭配…原因下面解释
模拟实现
其实就是一个循环队列, 在put
方法加入元素的时候如果队列是满的就进行阻塞, 在take
方法拿出元素的时候如果队列是空的也进行阻塞(使用wait
), 然后put
方法添加了一个元素之后, 使用notify
方法对take
正在阻塞的线程进行唤醒(随机唤醒), 下面是实现代码
/*** 自己实现一个阻塞队列* 1. 使用循环数组* 2. 使用wait-notify进行线程见的通信* 3. 关于wait的使用的while机制*/
public class MyBlockingQueue {// 我们定义这个阻塞队列中的元素是int类型private int capacity = 0;private int[] queue = null;// 构造方法public MyBlockingQueue(int capacity) {this.capacity = capacity;queue = new int[capacity];}// 定义队首尾的指针以及元素个数private int first = 0;private int last = 0;private int size = 0;// 判断队列是否为空private boolean isEmpty() {return size == 0;}// 判断队列是否是满的private boolean isFull() {return size == capacity;}// put操作public void put(int val) throws InterruptedException {while (isFull()) {// 此时进入阻塞等待synchronized (this) {this.wait();}}queue[last] = val;last = (last + 1) % capacity;size++;// 随机唤醒一个线程synchronized (this) {this.notify();}}// take操作public int take() throws InterruptedException {while (isEmpty()) {// 此时进入阻塞等待synchronized (this) {this.wait();}}int res = queue[first];first = (first + 1) % capacity;size--;// 随机唤醒一个线程synchronized (this) {this.notify();}return res;}
}class Test {public static void main(String[] args) {// 对实现的队列进行测试MyBlockingQueue myBlockingQueue = new MyBlockingQueue(100);// 创建生产者线程进行测试Thread producer = new Thread(() -> {for(int i = 0; i < 1000; i++){try {myBlockingQueue.put(i);System.out.println("生产了元素: " + i);} catch (InterruptedException e) {e.printStackTrace();}}});// 创建消费者线程进行测试Thread consumer = new Thread(() -> {for(int i = 0; i < 1000; i++){try {Thread.sleep(1000);int getElem = myBlockingQueue.take();System.out.println("消费了元素: " + getElem);} catch (InterruptedException e) {e.printStackTrace();}}});// 启动两个线程producer.start();consumer.start();}
}
瞬间产出100个元素之后进行阻塞, 产出一个消耗一个…
为什么要使用while代替if
// put操作public void put(int val) throws InterruptedException {if(isFull()) {// 此时进入阻塞等待synchronized (this) {this.wait();}}queue[last] = val;last = (last + 1) % capacity;size++;// 随机唤醒一个线程synchronized (this) {this.notify();}}// put操作public void put(int val) throws InterruptedException {while (isFull()) {// 此时进入阻塞等待synchronized (this) {this.wait();}}queue[last] = val;last = (last + 1) % capacity;size++;// 随机唤醒一个线程synchronized (this) {this.notify();}}
我们分析一下两个相同的操作, 使用while
和if
的区别
这一张图片揭示了为什么使用wait
搭配while
使用更加合理
相关文章:

JavaEE-经典多线程样例
文章目录 单例模式设计模式初步引入为何存在单例模式饿汉式单例模式饿汉式缺陷以及是否线程安全懒汉式单例模式基础懒汉式缺陷以及是否线程安全懒汉式单例模式的改进完整代码(变量volatile) 阻塞队列生产者消费者模型生产者消费者模型的案例以及优点请求与响应案例解耦合削峰填…...

从 HTML 到 CSS:开启网页样式之旅(五)—— CSS盒子模型
从 HTML 到 CSS:开启网页样式之旅(五)—— CSS盒子模型 前言一、盒子模型的组成margin(外边距):border(边框):padding(内边距):conten…...

数据分析(一): 掌握STDF 掌握金钥匙-码农切入半导体的捷径
中国的半导体行业必然崛起!看清这个大势,就会有很多机会。 今天,我们一起来了解一下半导体行业的一朵金花:STDF。 实际上这只是一种文件格式,但是当你熟练掌握解析这种文件的时候,你就已经打开在这个基础…...

HCIA-openGauss_1_4基本功能介绍
openGauss支持标准SQL SQL是用于访问和处理数据库的标准计算机语言,SQL标准的定义分成核心特性以及可选特性,绝大部分的数据库都没有100%支撑SQL标准。openGuass支持SQL2003标准语法,支持主备部署的高性能可用关系型数据库。openGauss数据库…...

医学临床机器学习中算法公平性与偏差控制简析
摘要 随着医疗领域中数据的不断积累和计算能力的提升,临床机器学习技术发展迅速,但算法不公平性和偏差问题凸显。本文深入探讨了临床机器学习算法公平性的重要性、概念与定义、在临床应用中的影响、偏差来源、降低偏差方法及提升公平性策略。通过对不同…...

Leetcode打卡:棋盘上有效移动组合的数目
执行结果:通过 题目:2056 棋盘上有效移动组合的数目 有一个 8 x 8 的棋盘,它包含 n 个棋子(棋子包括车,后和象三种)。给你一个长度为 n 的字符串数组 pieces ,其中 pieces[i] 表示第 i 个棋子的…...

生产看板到底在看什么?
说起生产看板,可能很多人脑海里冒出来的画面是:车间里一块挂在墙上的大板子,上面贴满了各式各样的卡片、表格,甚至还有几个闪闪发光的指示灯。但是,无论是精益生产方式代表——丰田,还是当下以“智能制造”…...

12,攻防世界simple_php
simple_php 题目来源:Cyberpeace-n3k0 题目描述: 小宁听说php是最好的语言,于是她简单学习之后写了几行php代码。 进入靶场 这段PHP代码是一个简单的web应用示例,让我们逐步分析这段代码: show_source(__FILE__);:这行代码会显示当前文件的…...

解决Jupyter Notebook无法转化为Pdf的问题(基于Typora非常实用)
笔者在完成各项作业和做笔记时,经常用到jupyter notebook;其因为可以同时运行python并提供格式化的数字公式的输入方式,得到了广大用户的喜爱。 当我们想要将.ipynb文件导出为pdf时,有两种常用方法。 1.Ctrlp 2.通过File ->…...

齐护机器人ModbusRTU RS485转TTL通信模块与ESP32 Arduino通信可Mixly的图形化编程Scratch图形化编程
齐护机器人ModbusRTU RS485-TTL通信模块 一、概念理解 Modbus协议是一种由Modicon公司(现为施耐德电气Schneider Electric)于1979年发表的网络通信协议,旨在实现可编辑逻辑控制器(PLC)之间的通信。 1.1 什么是Mod…...

python学习笔记15 python中的类
上一篇我们介绍了python中的库 ,学习了一些常见的内置库。详细内容可点击–>python学习笔记14 python中的库,常见的内置库(random、hashlib、json、时间、os) 这一篇我们来看一下python中的类 创建一个类 class 类的名称():de…...

PMP–一、二、三模、冲刺–分类–10.沟通管理
文章目录 技巧十、沟通管理 一模10.沟通管理--1.规划沟通管理--文化意识--军事背景和非军事背景人员有文化差异5、 [单选] 项目团队由前军事和非军事小组成员组成。没有军事背景的团队成员认为前军事团队成员在他们的项目方法中过于结构化和僵化。前军事成员认为其他团队成员更…...
android-studio开发第一个项目,并在设备上调试
恭喜你成功安装并配置好了 Android Studio!下面是开发你的第一个 Android 项目并在设备上调试的详细步骤: 1. 启动 Android Studio 首先,启动 Android Studio。你可以通过以下几种方式启动: 使用桌面快捷方式(如果已…...
springboot/ssm线上教育培训办公系统Java代码web项目在线课程作业源码
springboot/ssm线上教育培训办公系统Java代码web项目在线课程作业源码 基于springboot(可改ssm)htmlvue项目 开发语言:Java 框架:springboot/可改ssm vue JDK版本:JDK1.8(或11) 服务器:tomcat 数据库&…...
Spring 依赖 详解
Spring 依赖详解 在 Spring 框架中,依赖 是指一个对象(Bean)需要另一个对象(Bean)来完成其功能的情况。Spring 通过 依赖注入(Dependency Injection, DI) 和 控制反转(Inversion of…...
千益畅行,旅游卡有些什么优势?
千益畅行共享旅游卡是一种创新的旅游服务模式,旨在通过整合各类旅游资源,为用户提供一站式的旅游解决方案。这张旅游卡支持2至6人同行,涵盖了接机、酒店、用餐、大巴、导游、景区门票等服务,用户只需自行承担往返交通费用即可享受…...
Ubuntu24 cgroupv2导致rancher(k3s)启动失败的处理
方案一: 修改系统镜像为ubuntu18 方案二: 修改当前系统的cgroup版本,由v2改成v1 修改步骤: 1、查看当前cgroup版本 stat -fc %T /sys/fs/cgroup cgroup v2,输出结果为cgroup2fs cgroup v1,输出为tm…...
学习CSS第二天
学习文章目录 一.内部样式 一.内部样式 写在 html 页面内部,将所有的 CSS 代码提取出来,单独放在 <style> 标签中 语法: <style> h1 { color: red; font-size: 40px; } </style>注意点: <style> 标签理…...

2021数学分析【南昌大学】
2021 数学分析 求极限 lim n → ∞ 1 n ( n + 1 ) ( n + 2 ) ⋯ ( n + n ) n \lim_{n \to \infty} \frac{1}{n} \sqrt [n]{(n+1)(n+2) \cdots (n+n)} n→∞limn1n(n+1)(n+2)⋯(n+n) lim n → ∞ 1 n ( n + 1 ) ( n + 2 ) ⋯ ( n + n ) n = lim n → ∞ ( n + …...

单端和差分信号的接线法
内容来源:【单端信号 差分信号与数据采集卡的【RSE】【 NRES】【 DIFF】 模式的连接】 此篇文章仅作笔记分享。 单端输入 单端信号指的是输入信号由一个参考端和一个信号端构成,参考端一般是地端,信号就是通过计算信号端口和地端的差值所得…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...

分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...

面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...
Python竞赛环境搭建全攻略
Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型(算法、数据分析、机器学习等)不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...