JAVA中多线程的经典案例
文章目录
- 一、线程安全的单例模式
- (一)饿汉模式
- (二)懒汉模式
- 二、阻塞队列
- (一)生产者消费者模型
- (二)阻塞队列
- (三)自定义阻塞队列
- 三、定时器
- (一)定时器的使用
- (二)自定义定时器
- (三)自定义定时器缺陷分析
- 四、线程池
- (一)用户态vs内核态
- (二)标准库线程池
- (三)自定义线程池
一、线程安全的单例模式
单例模式即代码中的某个类只能有一个实例,不能有多个 。单例模式有两种主要实现方式,饿汉模式和懒汉模式
(一)饿汉模式
在类加载阶段就会直接创建唯一实例,步骤如下
- 首先使用static创建一个实例,并立即进行实例化
- 经过static修饰的成员,准确来说应该称为“类成员”,修饰为对应的类属性或类方法
一个JAVA程序中,一个类对象存在一份(类名.class文件被加载到JVM内存中生成的一个对象),那么类成员(static修饰的成员)也存在一份。
-
为了防止再重新new一个实例,需要把构造方法设置成private
-
提供能拿到唯一实例的方法
class Singleton{private static Singleton instance = new Singleton();private Singleton(){}public static Singleton getInstance(){return instance;}
}
public class Demo {public static void main(String[] args) {//Singleton singleton = new Singleton();//errorSingleton singleton = Singleton.getInstance();}
}
线程安全看多个线程同时调用getInstance()方法时是否会出现错误,饿汉模式中的getInstance仅读取变量内容,如果多个线程同时读一个变量,此时线程是安全的
(二)懒汉模式
懒汉模式不会立即初始化实例,而是等到使用的时候再创建。在饿汉模式的基础上进行了修改
class Singleton2{private static Singleton2 instance = null;private Singleton2(){}public static Singleton2 getInstance(){if(instance == null){instance = new Singleton2();}return instance;}
}
public class Demo {public static void main(String[] args) {Singleton2 singleton2 = Singleton2.getInstance();}
}
懒汉模式中的getInstance()方法既包含了读操作,又包含了修改操作,是非原子性的,可能导致实例被创建出多份,存在线程不安全问题
可以通过加锁将操作打包成原子的来保障线程安全,这里的类对象作为锁对象
class Singleton2{private static Singleton2 instance = null;private Singleton2(){}public static Singleton2 getInstance(){synchronized (Singleton2.class){if(instance == null){instance = new Singleton2();}}return instance;}
}
线程不安全是发生在instance被初始化前,未初始化时多线程调用getInstance可能同时涉及到读和修改,一旦instance初始化之后,仅存在读操作,线程也就安全了。这样初始化后及时线程安全了,每次调用getInstance方法都需要进行加锁,从而产生锁竞争的问题
对应的改进方案,再添加一个判定条件,让instance初始化之前进行加锁,初始化后就不进行加锁了。里层的 if 条件不能进行省略,在两个 if 条件判断的时间差内,可能存在instance的修改操作,若去掉里层的 if 那么就没有将读写操作进行原子性打包
public static Singleton2 getInstance(){if(instance == null){synchronized (Singleton2.class){if(instance == null){instance = new Singleton2();}}}return instance;}
如果多个线程块都去调用 getInstance 方法,大量的读操作会产生编译器优化,读内存的操作变成了读寄存器的操作,这样如果第一个线程完成了对 instance 的修改,后续的线程中第一个 if 读取并不会感知到这个变化,会引起多余的加锁操作,但是内层的 if 不会引起到误判,不会因此创建多个实例,因此给instance假设volatile修饰防止多加锁操作
最终的代码如下
class Singleton2{private static volatile Singleton2 instance = null;private Singleton2(){}public static Singleton2 getInstance(){if(instance == null){synchronized (Singleton2.class){if(instance == null){instance = new Singleton2();}}}return instance;}
}
二、阻塞队列
阻塞队列同样是一个先进先出的队列,相对于普通队列来说,阻塞队列线程安全,且产生阻塞效果
1.如果队列为空,尝试出队列,就会出现阻塞,阻塞到队列不为空为止
2.如果队列为满,尝试入队列,也会出现阻塞,阻塞到队列不满为止
(一)生产者消费者模型
假设有两个服务器A、B。A 作为入口服务器直接接收用户的网络请求,B 作为应用服务器来给A提供一些数据,A B之间直接进行交互,那么就需要A B互相知道对应的接口,这样耦合性较高,因此使用生产者消费者模型就来降低耦合性
A只需要和阻塞队列进行交互,不需要关注B,B也只需要和阻塞队列进行交易,若B发生异常或进行了替换,
使用生产者消费者模型的优点:
-
优点1:让多个服务器之间充分的解耦
-
优点2:能够对请求进行“削峰填谷”
-
如果未使用生产者消费者模型时,请求如果突然暴涨,A作为入口服务器计算量很轻,B作为应用服务器计算量可能很大,需要的系统资源也很多,请求增多后需要的资源进一步增加,会出现程序崩溃。
使用生产者消费者模型后,阻塞队列的请求暴涨,B仍然按照原来的速度消费数据,不会因为A的暴涨产生影响 -
当没有数据请求的时候,B也会继续处理阻塞队列中积压的数据
在实际开发过程中的阻塞队列并不是一个简单的数据结构,而是一个/一组专门的服务器程序,不仅又阻塞队列的功能,而且还有数据化持久存储、支持多个数据通道、支持多节点 冗余备份、支持面板管理方便参数配置…此时的队列称为“消息队列”
(二)阻塞队列
下面是JAVA标准库中的阻塞队列BlockingDeque
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;public class Demo {public static void main(String[] args) throws InterruptedException {BlockingDeque<String> queue = new LinkedBlockingDeque<>();queue.put("hello");//入队列String s = queue.take();//出队列}
}
(三)自定义阻塞队列
使用数组的方式实现阻塞队列,判断队列空还是满一般采用浪费一个额外空间,或者另创建一个size变量计数的方法,本例选择另创建一个size变量计数的方法。
- put的阻塞条件是队列为满,put中的wait要由take来唤醒,只要take成功一个元素,队列就不满
- take的阻塞条件是队列为空,队列为空后由put来唤醒,只要put成功后,队列为非空
- notify在唤醒的时候是随机唤醒一个线程的
- 生产者较慢的时候,消费者就等待着生产者进行消费;消费者较慢的时候,生产者快速填满队列出现阻塞,生产者等待消费者消费后进行生产
class MyBlockQueue{//保存数据本体private int[] data = new int[1000];private int size = 0;private int head = 0;//队首private int tail = 0;//队尾private Object locker = new Object();//入队列public void put(int value) throws InterruptedException {synchronized(locker){if(size == data.length){//return;locker.wait();}data[tail] = value;tail++;if(tail >= data.length){tail = 0;}size++;locker.notify();}}//出队列public Integer take() throws InterruptedException {synchronized(locker){if(size == 0){//return null;locker.wait();}//取出head位置的元素int ret = data[head];head++;if(head >=data.length){head = 0;}size--;//take 成功之后唤醒 put 中的等待locker.notify();return ret;}}
}
public class Demo {private static MyBlockQueue queue = new MyBlockQueue();public static void main(String[] args) {Thread producer = new Thread(()->{int num = 0;while(true){try {System.out.println("生产者:"+num);queue.put(num);num++;} catch (InterruptedException e) {e.printStackTrace();}}});producer.start();Thread customer = new Thread(()->{while(true){try {int num = queue.take();System.out.println("消费着:"+num);//Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}});customer.start();}
}
三、定时器
定时器是用于安排任务在将来的某个时间执行的一种工具,即等待一定时间后,唤醒并执行某个之前设定好的任务,系统拥有自带的定时器例如sleep(指定休眠是新建),join(休眠指定时间)
(一)定时器的使用
自己设定定时器使用到了java.util.Timer类,包含一个主要的方法 schedule(),涉及两个参数,第一个参数描述需要定时的任务,第二个参数设定要等待的时间,TimerTask类实现了Runnable接口
public void schedule(TimerTask task, long delay) {if (delay < 0)throw new IllegalArgumentException("Negative delay.");sched(task, System.currentTimeMillis()+delay, 0);}
(二)自定义定时器
-
创建一个专门的类来表示定时器中的任务(类比TimeTask类)
-
使用优先队列组织任务,即使用一定的数据结构将任务放到一起,在执行任务的时候,需要优先执行剩余时间最小的任务,涉及到的数据结构为堆,标准库中的数据结构PriorityQueue。
此处的队列要考虑线程安全问题,可能要在多个线程里面注册任务,同时还有一个专门的线程来取任务执行,此处的队列需要注意线程安全问题,因此采用PriorityBlockingQueue类,既带有优先级,又带有阻塞队列 -
实现schedule方法来注册任务到队列中
-
设置一个扫描线程,不断检查当前优先队列的队首元素,如果队首元素的 时间到了就执行任务,若没有到就将该任务返回队列
import java.util.PriorityQueue;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.PriorityBlockingQueue;class MyTask{//任务具体要干什么private Runnable runnable;//任务具体干什么,保存任务要执行的毫秒级时间戳private long time;///after是一个时间间隔,保存任务要执行的毫秒级时间戳public MyTask(Runnable runnable,long after){this.runnable = runnable;this.time = System.currentTimeMillis()+after;}public void run(){runnable.run();}public long getTime() {return time;}
}class MyTimer{private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();public void schedule(Runnable runnable,long delay){MyTask task = new MyTask(runnable,delay);queue.put(task);}public MyTimer(){Thread t = new Thread(()->{while (true){try {//首先取出队首元素MyTask task = queue.take();long curTime = System.currentTimeMillis();if(curTime < task.getTime()){//时间没到,将任务返回至队列queue.put(task);}else {//时间到了,执行这个任务task.run();}} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}
}
public class Demo {public static void main(String[] args) {MyTimer timer = new MyTimer();timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello timer");}},3000);System.out.println("main");}
}
(三)自定义定时器缺陷分析
-
缺陷一:MyTask没有指定比较规则
-
在上述的代码中,需要注意,设置优先队列的的类型为自定义MyTask类,并没有比较规则,因此需要实现这个Comparable接口,需要手动指定按照时间大小来比较
class MyTask implements Comparable<MyTask>{//任务具体要干什么private Runnable runnable;//任务具体干什么,保存任务要执行的毫秒级时间戳private long time;///after是一个时间间隔,保存任务要执行的毫秒级时间戳public MyTask(Runnable runnable,long after){this.runnable = runnable;this.time = System.currentTimeMillis()+after;}public void run(){runnable.run();}public long getTime() {return time;}@Overridepublic int compareTo(MyTask o) {return (int) (this.time- o.time);}
}
-
缺陷二:忙等现象
-
扫描线程不断检查当前优先队列的队首元素,直到达到可运行的时间,线程处于忙等状态,浪费CPU的资源。这里采用 wait(等待的时间) 的方式指定等待时间,不需要notify唤醒。
这里使用wait,不使用sleep是因为sleep中途不能被唤醒,如果在sleep中途插入了一个时间更近的任务,就没有办法执行该任务,因此使用wait,每次插入新的任务时唤醒扫描线程
class MyTimer{private Object locker = new Object();private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();public void schedule(Runnable runnable,long delay){MyTask task = new MyTask(runnable,delay);queue.put(task);//每次插入成功后都唤醒以下扫描线程,让线程重新检查队首的任务时间到了synchronized (locker){locker.notify();}}public MyTimer(){Thread t = new Thread(()->{while (true){try {//首先取出队首元素MyTask task = queue.take();long curTime = System.currentTimeMillis();if(curTime < task.getTime()){//时间没到,将任务返回至队列queue.put(task);//指定等待时间synchronized (locker){locker.wait(task.getTime()-curTime);}}else {//时间到了,执行这个任务task.run();}} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}
}
完整的代码如下
import java.util.PriorityQueue;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.PriorityBlockingQueue;class MyTask implements Comparable<MyTask>{//任务具体要干什么private Runnable runnable;//任务具体干什么,保存任务要执行的毫秒级时间戳private long time;///after是一个时间间隔,保存任务要执行的毫秒级时间戳public MyTask(Runnable runnable,long after){this.runnable = runnable;this.time = System.currentTimeMillis()+after;}public void run(){runnable.run();}public long getTime() {return time;}@Overridepublic int compareTo(MyTask o) {return (int) (this.time- o.time);}
}class MyTimer{private Object locker = new Object();private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();public void schedule(Runnable runnable,long delay){MyTask task = new MyTask(runnable,delay);queue.put(task);//每次插入成功后都唤醒以下扫描线程,让线程重新检查队首的任务时间到了synchronized (locker){locker.notify();}}public MyTimer(){Thread t = new Thread(()->{while (true){try {//首先取出队首元素MyTask task = queue.take();long curTime = System.currentTimeMillis();if(curTime < task.getTime()){//时间没到,将任务返回至队列queue.put(task);//指定等待时间synchronized (locker){locker.wait(task.getTime()-curTime);}}else {//时间到了,执行这个任务task.run();}} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}
}
public class Demo {public static void main(String[] args) {MyTimer timer = new MyTimer();timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello timer");}},3000);System.out.println("main");}
}
四、线程池
线程虽然比进程更轻量级,但是如果创建/销毁线程的频率进一步增加,所带来的开销也会比较大,采用线程池的方法,线程创建好放进线程池中,后续需要线程的时候直接从线程池中取,不需要向系统申请,且线程用完后不会还给系统,而是再次放入线程池以备下次使用。
(一)用户态vs内核态
程序代码在最上面的应用程序层来运行,这里的代码被称为“用户态”运行的代码,然而有些代码需要调用操作系统的API,进一步的逻辑就会在内核中执行,例如调用一个Aystem.out.println,本质上要经过write系统调用,进入到内核中,内核执行对应的逻辑,再控制台输出字符串。
在内核中运行的代码,称为"内核态",例如创建线程操作需要内核的支持,即在内核中创建PCB加到就绪链表中,Thread.start本质需要进入到内核态来运行,池子是用户态实现的,而把创建好的线程放到线程池中,整个过程就不需要涉及到内核态,使用用户态代码就能完成。
一般来说认为纯用户态的操作效率比经过内核态处理的操作要效率更高,代码进入了内核态后存在不可控的现象,
(二)标准库线程池
JAVA提供的标准库线程池 ThreadPoolExecutor 包含在并发相关的包java.util.concurrent内部,其创建线程池的构造方法如下
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
- int corePoolSize 表示核心线程数,int maximumPoolSize 表示最大线程数,包括核心线程和非核心线程。
- long keepAliveTime 表述了非核心线程的存活时间
- TimeUnit unit 表示keepAliveTime的时间单位
- BlockingQueue workQueue 任务队列,线程池会提供一个submit方法将任务放入到线程池任务队列中
- ThreadFactory threadFactory 线程工厂,描述线程是怎么创建出来的
- RejectedExecutionHandler handler,拒绝策略,当任务队列满了设定的 具体策略,包括忽略最新任务、阻塞等待、丢弃最旧任务等方式
对于设定的核心线程数量,需要根据实际的业务场景进行设定,根据不同的线程池的线程数,来观察程序处理任务的速度,和程序持有的CPU的占用率。在实际编程中,因为不同类型的程序,单个任务里CPU上的计算时间和阻塞时间是不相同的,因此需要寻求一个程序执行速度和CPU占用合理的平衡点
- 当线程数多时,整体的速度会变快,但是CPU的占用率也会高;当线程数少时,整体的速度会变慢,但是CPU的占用率也会下降。
- 需要考虑CPU是占用率不能太高,对于线上服务器来说需要设定一定的CPU冗余,以应对随时可能突发的情况,例如需求量暴涨等。
标准库中提供了一个简化版本的线程池Executors,本质上是对ThreadPoolExecutor进行了一个封装,Executors类创建线程池的时候实际就是调用ThreadPoolExecutor类的构造方法来创建,Executors提供了一些默认的参数,如下所示
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Demo {public static void main(String[] args) {//创建一个固定线程数目的线程池,参数指定了线程个数ExecutorService pool = Executors.newFixedThreadPool(10);//创建一个自动扩容的线程池,会根据任务来自动进行扩容//Executors.newCachedThreadPool();//创建一个只有一个线程的线程池//Executors.newSingleThreadExecutor();//创建一个带有时器功能的线程池,类似于Timer//Executors.newScheduledThreadPool();for(int i =0;i<100;i++){pool.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello threadpool");}});}}
}
(三)自定义线程池
创建自定义线程池的步骤如下
- 使用Runnable来描述任务
- 直接使用BlockingQueue阻塞队列来组织任务
- 能够描述工作线程Worker,线程循环从队列中取任务并执行
- 创建一个数据结构来组织若干个Worker线程
- 创建一个方法实现往线程池中添加任务
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;class MyThreadPool{//1.描述一个任务,直接使用Runnable,不需要额外创建类了//2.使用一个数据结构来组织若干任务private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();//3.描述一个线程,工作线程的功能就是从任务队列中取任务并执行static class Worker extends Thread{private BlockingQueue<Runnable> queue = null;public Worker(BlockingQueue<Runnable> queue) {this.queue = queue;}@Overridepublic void run() {//需要能够拿到上面的队列while(true){try {//循环获取任务队列中的任务//如果队列为空就直接阻塞,如果队列不为空,就获取里面的内容Runnable runnable = queue.take();//获取到之后就执行任务runnable.run();} catch (InterruptedException e) {e.printStackTrace();}}}}//4.创建一个数据结构来组织若干个线程private List<Thread> workers = new ArrayList<>();public MyThreadPool(int n){//在构造方法中创建若干个线程放入上面的数组中for (int i=0;i<n;i++){Worker worker = new Worker(queue);worker.start();workers.add(worker);}}//5.创建一个方法,使程序员能够将任务放到线程池中public void submit(Runnable runnable){try {queue.put(runnable);} catch (InterruptedException e) {e.printStackTrace();}}}
public class Demo {public static void main(String[] args) {MyThreadPool pool = new MyThreadPool(10);for(int i=0;i<100;i++){pool.submit(new Runnable() {@Overridepublic void run() {System.out.println("Hello ThreadPool");}});}}
}
相关文章:
JAVA中多线程的经典案例
文章目录 一、线程安全的单例模式(一)饿汉模式(二)懒汉模式 二、阻塞队列(一)生产者消费者模型(二)阻塞队列(三)自定义阻塞队列 三、定时器(一&am…...
国产三维CAD皇冠CAD(CrownCAD)在「轨道交通行业」建模教程:轨道列车
在轨道交通行业,复杂系统集成、大规模装配验证与跨地域协同设计始终是核心痛点。传统设计工具难以应对动辄百万级零部件的装配挑战,且数据孤岛、版本混乱、硬件成本高昂等问题长期制约行业数字化转型。皇冠CAD(CrownCAD)作为国产云…...
Linux 日常运维命令大全
Linux 作为一种开源操作系统,在服务器运维中扮演着重要角色。掌握常用的 Linux 命令对于运维人员而言至关重要。本文将整理一份 Linux 服务器运维常用命令大全,帮助你在日常工作中提高效率和准确性。 1. 基础命令 基础命令是Linux操作的起点࿰…...
安全测试报告模板
安全测试报告 一、项目概况 项目名称XX智慧医疗平台被测系统版本V2.3.1测试类型渗透测试漏洞扫描测试时间2024年2月15-20日测试标准OWASP TOP 10 2021 二、测试环境 生产环境镜像: - 服务器:CentOS 7.9 Tomcat 9 - 数据库:MySQL 8.0集群…...
树莓派超全系列教程文档--(31)config.txt常用选项介绍
config.txt常用选项介绍 常用选项常用显示选项hdmi_enable_4kp60 常用硬件配置选项camera_auto_detectdisplay_auto_detectdtoverlaydtparamarm_boostpower_force_3v3_pwm 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 常用选项 常用显示选项 …...
算法-堆+单调栈
堆 首先堆在我们的Java中我们的是一个优先队列类 PriorityQueue 然后我们要弄最大堆和最小堆 最大堆: PriorityQueue<Integer> pq new PriorityQueue<Integer>((a, b) -> b - a); 最小堆: PriorityQueue<Integer> pq new P…...
Charles破解 激活码 Java
第一步,下载charles Download a Free Trial of Charles • Charles Web Debugging Proxy 第二部,生成key,这里使用的是java代码 import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Random;public class test {private static final int ROUNDS 12;p…...
线上蓝桥杯比赛环境配置
1.编译环境(以下是JAVA示例) Java软件开发编程环境 链接: https://pan.baidu.com/s/1JRNx0bkgHmPqQhANSFBNkQ 提取码: ftgw 下载对应的编译器和jdk以及对应的API文档 解压后把eclipse发送到桌面方便使用 2.录屏软件,我这边选择的是OBS St…...
民办生从零学C的第十一天:操作符
每日励志:我们可以随时的转身,但是决不能后退。 一.操作符的分类 算术操作符:、-、*、/、% 移位操作符:<<、>> 位操作符:&、|、^ 赋值操作符:、、-、*、/、%、<<、>>、&…...
疑难问题解决(2)
(1):在k230开发板中,ubuntu操作系统中的文件夹中的k230_sdk文件夹与canmv_k230文件夹的区别,以及 /home/ubuntu/canmv_k230/src/rtsmart/rtsmart/userapps/07_driver_hello 与 /home/ubuntu/k230_sdk/src/big/rt-smart…...
第六章 进阶04 尊重
本周周会给大家讲的议题是:尊重。 用“尊重”给周报文件冠名,周会中打开这个文件,就可以在标题中醒目地看到,加深了大家的印象、勾起了大家的好奇心。坚持长期事项的同时,偶尔也灵光一现给团队管理加入一些小插曲&…...
Android 12.0 framework实现对系统语言切换的功能实现
1.前言 在12.0的系统rom定制化开发过程中,在定制某些接口的过程中,需要通过系统提供接口,然后实现对系统语言的切换 功能实现,接下来分析下系统中关于系统语言切换的相关功能 2.framework实现对系统语言切换的功能实现的核心类 frameworks/base/core/java/android/app/IA…...
Origin LabTalk
之前用惯了matplotlib绘图,出于科研需要部分图用origin来画,但是还是想着要结合python来处理数据更加的方便,经过一番捣鼓发现origin自带有labtalk,并且还带有python的环境,真可谓是NB的很。 若能由程序代劳,何必亲手?…...
基于VS Code 为核心平台的python语言智能体开发平台搭建
以下是基于 VS Code 为核心平台,整合 Node-RED、Gradio、Docker Desktop 的智能体可视化开发平台优化方案,聚焦工具链深度集成与开发效率提升: 一、核心架构设计 #mermaid-svg-f8l9kYPAlJ2TlpGF {font-family:"trebuchet ms",verd…...
Python 创意:AI 图像生成
一、基于 Stable Diffusion 的本地创意创作 Stable Diffusion 是开源图像生成模型的代表,通过 Python 结合diffusers库,可实现本地图像生成。 1. 环境搭建 首先,安装必要的库: pip install diffusers transformers torch若使用 GPU 加速,需安装对应版本的 CUDA 和 cuD…...
vue3 传参 传入变量名
背景: 需求是:在vue框架中,接口传参我们需要穿“变量名”,而不是字符串 通俗点说法是:在网络接口请求的时候,要传属性名 效果展示: vue2核心代码: this[_keyParam] vue3核心代码&…...
Skipped breakpoint at ... because of stepping in another thread问题分析
在Java多线程应用程序的调试过程中,开发者可能会遇到“Skipped breakpoint at … because of stepping in another thread”这样的提示。这通常是因为调试器在处理多线程操作时,忽略了某个断点。本文将详细分析这一问题的原因,并提供有效的解…...
MATLAB脚本实现了一个转子系统的参数扫描和分岔分析
% 参数扫描范围 clc; clear; close all;S_values 500:200:20000; % 转速范围% 定义系统参数 N 5; % 质量点数量 num_nodes N; % 节点数 num_dofs_per_node 4; % 每个节点的自由度数 num_elements num_nodes-1; % 单元数 total_dofs num_nodes * num_dofs_per_node; % 总自…...
基于Flask的AI工具聚合平台技术解析
基于Flask的AI工具聚合平台技术解析 一、项目架构设计 本系统采用经典的三层架构模式,通过Mermaid架构图可清晰看到数据流向: 用户请求通过浏览器发送至Flask服务器路由系统解析请求路径模板引擎动态渲染页面静态资源提供样式支持独立数据模块实现内容…...
AUTOSAR图解==>AUTOSAR_SWS_CryptoInterface
AUTOSAR 加密接口(Crypto Interface)详解 基于AUTOSAR标准4.4.0的加密接口规范详细分析与图解 目录 概述 1.1 加密接口的作用与位置 1.2 主要术语解释架构设计 2.1 加密接口架构 2.2 组件关系内部结构 3.1 类结构 3.2 配置项运行流程 4.1 加密请求处理流程 4.2 同步与异步处理…...
GCD算法的学习
GCD算法的学习 学习了前辈wzx15927662183的文章GCD算法精讲-CSDN博客 介绍 GCD通常用来求两个数的最大公约数 算法的核心:gcd(a,b) gcd(b,a % b) 证明的思路: 证明 gcd(a, b) gcd(b, a % b) 的思路: 设 a > b 1. 构造 a % b : 设 …...
完美解决浏览器不能复制的问题(比如赛氪网的中题库练习题)
仅供复制题库题目进行打印学习使用! 最近想把赛氪网题库中的题目打印出来做练习,发现题库中的题目不能复制,不能在试卷上勾画标记太难受了,而且不能留作材料以后复习,故出此策。 而且CtrlP打印出的pdf会缺少题目。(我…...
Java 爬虫按关键字搜索淘宝商品:实现与优化
在电商领域,获取淘宝商品信息对于市场分析、价格监控和竞争情报等方面具有重要意义。Java 爬虫技术为我们提供了一种高效、自动化的方式来按关键字搜索淘宝商品。本文将详细介绍如何使用 Java 爬虫按关键字搜索淘宝商品,并提供完整的代码示例。 一、准备…...
build.gradle task copyJarToDesktop
build.gradle task copyJarToDesktop 构建完,拷贝jar包到指定文件夹AAA,例如:桌面,方便拉到宝塔发布 build.gradle plugins {id org.springframework.boot }jar {enabled false // 不生成 plain.jar }bootJar {archiveFileNa…...
Git合并分支的两种常用方式`git merge`和`git cherry-pick`
Git合并分支的两种常用方式git merge和git cherry-pick 写在前面1. git merge用途工作方式使用git命令方式合并使用idea工具方式合并 2. git cherry-pick用途工作方式使用git命令方式合并使用idea工具方式合并 3. 区别总结 写在前面 一般我们使用git合并分支常用的就是git mer…...
基于n8n的AI应用工作流原理与技术解析
基于n8n的AI应用工作流原理与技术解析 在AI技术深度融入企业数字化转型的今天,开源工作流自动化工具n8n凭借其灵活的架构和强大的集成能力,成为构建智能自动化流程的核心引擎。本文将从技术原理、AI融合机制、典型应用场景三个维度,解析n8n在…...
Day3-UFS深入学习路线
UFS 学习链接1:UPUI数据包格式 学习链接2:UPUI数据包详解 学习链接3:UFS电源及低功耗 一、基础准备阶段 1.理解存储技术背景 学习NAND Flash基本原理(SLC/MLC/TLC、读写擦除操作、磨损均衡)。对比其他存储协议&…...
广东2024信息安全管理与评估一阶段答案截图
2023-2024 学年广东省职业院校技能大赛 高等职业教育组 信息安全管理与评估 赛题一 模块一 网络平台搭建与设备安全防护 一、 比赛时间 本阶段比赛时间为 180 分钟。 二、 赛项信息 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 第一…...
8.Rust+Axum 数据库集成实战:从 ORM 选型到用户管理系统开发
摘要 深入探讨 RustAxum 数据库集成,包括 ORM 选型及实践,助力用户管理系统开发。 一、引言 在现代 Web 应用开发中,数据库集成是至关重要的一环。Rust 凭借其高性能、内存安全等特性,与 Axum 这个轻量级且高效的 Web 框架结合…...
题解:CF886E Maximum Element
正难则反,考虑长度为 i i i 的排列得到正确的结果的方案数。 设 d p i dp_i dpi 表示长度为 i i i 的排列直到循环完也没有提前 return 的方案数。考虑 i i i 所放置的位置,由于不会提前 return,也就说明该数字所在的位置为 [ i − k…...
OPC Client第3讲(wxwidgets):wxFormBuilder;基础框架;事件处理
wxwidgets开源桌面软件框架使用 - 哔哩哔哩 wxwidgets跨平台GUI框架使用入门详解_哔哩哔哩_bilibili 一、wxwidgets配置【见上一讲五、】 二、安装wxFormBuilder 1、wxFormBuilder介绍、安装 wxFormBuilder是一个开源的GUI设计工具,支持C、Python等语言&#…...
20250418项目接入scalar
scalar官网地址 scalar-dotnet文档地址 1. 引入nuget包 这里必须是2.1.* 以上 否则不支持多库 <PackageReference Include"Scalar.AspNetCore" Version"2.1.16" />2. 引入命名空间 using Scalar.AspNetCore;3. 使用scalar var documents new[] {…...
数控铣床自动上下料机械手控制装置设计
一、引言 在数控铣床加工过程中,实现自动上下料能够提高生产效率、降低劳动强度、减少人为因素对加工质量的影响。设计一款高效、可靠的数控铣床自动上下料机械手控制装置,是实现数控铣床自动化加工的关键。 二、控制装置设计要求 自动化程度…...
STM32F407的引脚说明
当笔记站 引脚说明在STM32F407数据手册中的48页到71页,下载地址: https://www.stmcu.com.cn/Designresource/detail/document/696193?auto_download1 以下是在图片转表格得到的东西 Pinouts and pin description …...
STM32单片机入门学习——第41节: [12-1] Unix时间戳
写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做! 本文写于:2025.04.18 STM32开发板学习——第41节: [12-1] Unix时间戳 前言开发板说明引用解答和科普一…...
使用Pydantic优雅处理几何数据结构 - 前端输入验证实践
使用Pydantic优雅处理几何数据结构 - 前端输入验证实践 一、应用场景解析 在视频分析类项目中,前端常需要传递几何坐标数据。例如智能安防系统中,需要接收: 视频流地址(rtsp_video)检测区域坐标点(point…...
【Hot100】41. 缺失的第一个正数
目录 引言缺失的第一个正数初始理解问题方法一分析:排序后遍历方法二分析:辅助数组寻找满足条件的解法代码实现验证例子复杂度分析 🙋♂️ 作者:海码007📜 专栏:算法专栏💥 标题:【…...
FairMOT算法详解
FairMOT(Fairness in Detection and Re-Identification for Multi-Object Tracking)是一种基于联合学习(Joint Learning)的多目标跟踪(MOT)算法,由中科院自动化所团队提出。其核心思想是通过单阶段网络同时完成目标检测和重识别(Re-ID)特征提取,解决了传统两阶段方法…...
java线程池原理及使用和处理流程
实际测试使用如下: package com.study;import java.util.concurrent.*;/*** 线程池作用:* 1、线程的复用* 2、资源管理* 3、任务调度* --------------执行过程--------------* 第1-3个任务进来时,直接创建任务并执行* 第4-8个任务进来时&…...
奖学金排序问题
#include <bits/stdc.h> using namespace std;const int N 305; // 定义最大学生人数为305// 定义学生结构体,包含语文、数学、英语成绩、总分以及学生编号 struct node {int yuwen; // 语文成绩int mat_h; // 数学成绩int english; // 英语成绩i…...
useMemo + memo + useContext 性能优化实战:从无感重渲染到丝滑体验
在 Vue 中我们可能依赖 Vuex computed 进行状态共享和性能优化,而在 React 里呢?不需要用 Redux,靠 useContext、memo、useMemo 三剑客就能构建高性能组件通信方案! 🧩 useContext 再回顾:状态共享不等于性…...
集合框架--Set集合详解
set集合 set 系列集合特点: 无序:存或取的元素的顺序可能是一致的,也可能不是 不重复:集合中不能存储重复的元素,我们可以利用这个特性去重 无索引:我们不可以通过索引获得set中的每一个元素 Set接口没…...
git -- 对远程仓库的操作 -- 查看,添加(与clone对比),抓取和拉取,推送(注意点,抓取更新+合并的三种方法,解决冲突,对比),移除
目录 对远程仓库的操作 介绍 查看 (git remote) 介绍 查看详细信息 添加(git remote add) 介绍 与 git clone对比 从远程仓库中抓取与拉取 抓取(git fetch) 拉取(git pull) 推送(git push) 介绍 注意 抓取更新合并的方法 git fetch git merge 解决冲突 git …...
Hadoop的三大结构及其作用
Hadoop 的三大核心结构及其作用如下: 1. 分布式文件系统(HDFS,Hadoop Distributed File System) 作用: 海量数据存储:提供高吞吐量、高容错性的分布式存储能力,支持存储 TB/PB 级的大规模数据…...
Java学习笔记--多态:多态的介绍,多态的基本使用,多态的条件下成员的访问特点,多态的好处
目录 1.多态的介绍 2.多态的基本使用 编辑 3.多态的条件下成员的访问特点 3.1成员变量 3.2成员方法 4.多态的好处(为什么学多态) 1.问题描述: 2.多态方式和原始方式new对象的优缺点: 一.多态的介绍 1.前提:a.必须有子父类继承或者接口实现关系b.必须有方法的重写(没…...
使用Python设置Excel单元格边框
在数据驱动的业务场景中,自动化设置Excel单元格边框成为提升数据处理效率的关键环节。通过程序化控制边框样式,不仅能确保海量报表格式的统一性,还能通过粗细、虚实等视觉元素强化数据逻辑层次。当面对动态更新的分析报告时,代码驱…...
ES中常用的Query和查询作用,以及SpringBoot使用实例
ES中常用的Query和查询作用,以及 SpringBoot 使用实例 文章目录 ES中常用的Query和查询作用,以及 SpringBoot 使用实例MatchAllQueryTermQueryBoolQueryRangeQueryMatchQueryMultiMatchQueryTermsQueryPrefixQueryWildcardQueryRegexpQueryFuzzyQueryDis…...
美信监控易告警:功能强大
美信监控易是一款功能强大的运维管理软件,其告警功能在保障系统稳定运行方面发挥着重要作用。 一、运维行业背景 随着信息技术的快速发展,企业的信息化程度越来越高,对 IT 系统的依赖也日益增强。IT 系统的稳定运行直接关系到企业的业务正常…...
字符串系列一>最长回文子串
目录 题目:解析:代码: 题目: 链接: link 解析: 代码: class Solution {public String longestPalindrome(String s) {char[] ss s.toCharArray();int n ss.length;int begin 0;//返回结果的起始字符串…...
CAPL编程系列_02
1_CAPL 中的运算符 在CAPL(CANoe/CANalyzer Programming Language)中,运算符用于执行各种运算操作,类似于其他编程语言。CAPL中的运算符可以分为以下几类: 1. 算术运算符 算术运算符 加法运算符 - 减法运算符*乘法运…...