JAVA EE_多线程-初阶(二)
1.线程安全
1.1.观察线程不安全
实例:
package thread;
public class text18 {//定义一个成员变量count,初始值为0private static int count=0;public static void main(String[] args) throws InterruptedException {Thread t1=new Thread(()->{for(int i=0;i<5000;i++){count++;}});Thread t2=new Thread(()->{for(int i=0;i<5000;i++){count++;}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}
}
以上代码中,我们理想的count结果是为10000,但是我们看下真实情况。
结果1:
结果2:
可以看到,两个运行结果不仅不是10000,而且两个运行结果还不相同,这便是线程不安全。
1.2.线程安全的概念
一段代码,在多线程的运行下,如果它运行出来的结果与单线程运行出来的结果相同时,那么我们就可以说这个线程是安全的。
1.3.线程不安全的原因
线程随机性
- 多线程的调度是随机性的,这个随机性就是线程不安全的主要因素。
- 线程调度在一个环境下,它执行的顺序是不相同的。
修改公共数据
例如1.1中,两个线程修改一个的count变量。
这时候的count变量就是可以被多个线程修改的“公共数据”。
原子性
什么是原子性
我们可以把一段代码想象成一个的厕所,这时候张三去上厕所没把门锁住,正在进行到一半,老六进来了在张三背后说我也要上厕所,这时候张三就给老六吓得强行打断施法,并对老六破口大骂。
同时我们把1.1的例子带入到张三老六身上,我们就可以说1.1例子中的代码没有原子性;
但如果张三把门锁了,也就是t2的线程等t1的线程运行完之后再运行,我们就可以说这段代码有原子性。
我们会把以上现象称为“同步互斥”,表示这个操作是互相排斥的。
一条JAVA语句不一定是一条指令
在1.1的例子中,count就有三条指令
- load 把内存中的值加载到寄存器中
- add 在寄存器中进行+1的操作
- save 将寄存器中操作过后的值放回内存中
不保证原子性会带来什么问题
例如1.1中,最后的cont就达不到我们理想中的数值
其原因就是t2的线程在t1还没完整的运行load,add,save这三个指令后就打断t1,使它得出错误的结果。
可见性
可见性是指,一个线程中的变变量能够被其他线程所看到。
1.4.解决之前的线程不安全问题
加个synchroized关键字
package thread;
public class text19 {//创建count成员变量public static int count=0;//创建静态的Object类的lock(锁)public static final Object lock=new Object();public static void main(String[] args) throws InterruptedException {Thread t1=new Thread(()->{for(int i=0;i<5000;i++){//使用synchronized来锁定lock对象synchronized(lock){count++;} }});Thread t2=new Thread(()->{for(int i=0;i<5000;i++){//使用synchronized来锁定lock对象synchronized(lock){count++;} }});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}
}
2.synchroized 关键字
2.1.synchroized的特征
互斥
synchroized具有互斥性,当一个线程使用synchroized锁住一个对象,同时另一个线程也用synchroized锁住同一个对象,这时候另一个线程就会堵塞。
可以把一个线程想象成一个厕所,当李四进入厕所时,他就会使用sychroized将门锁住,这时候王五想上厕所,那么因为门给锁住了,王五只能在门外,等李四上完之后,王五就获得了李四的锁,然后就进去上厕所再把门锁上,就不会像上文中张三老六发生尴尬的现象。
同理,例如1.1例子中,如果两个线程都用sychroized锁住了同一个对象,那么只能等t1线程解锁运行完之后,然后t2获得了锁再运行。
注意:
在上一个线程运行完解锁之后,如果有多个线程,那么系统会随机唤醒某个线程,而不是按照写代码的顺序唤醒,例如如果有t1,t2,t3线程,在t1线程解锁之后,等待的t2和t3线程中的其中一个就会获得锁,可能是t2也可能是t3。
可以理解为李四上完厕所出来手上拿着锁,在门外的张三和老六都想上厕所,然后他们就开始了争夺锁,最后可能是老六得到了锁也可能是张三得到了锁,但不管是谁得到了锁,到了最后他们都能成功的上完厕所~~
可重入
synchronized对于同一个线程来说是可重入的,就是可以写多条synchronized语句,不会造成死锁的情况。
死锁:
死锁就是写入一条syncronized语句时,还没解锁就写入另一条synchronized,这时候如果要解锁里面的syncronized就需要先解锁外面一层的synchronized,然而理想是丰满的现实是残酷的,有两个条锁的情况下我们并不能把这两条锁都解除掉,当第一个锁解除后这条线程也就停止了运行,第二个锁就解不了了,这就造成了死锁现象。
但是JAVA的synchronized是可重入锁,这就为我们解决了死锁的问题。
在可重入中,包含了“线程持有者”和“计时器”两个信息
“线程持有者”记录了当前的前程,当发现有线程占用锁时,但那个占用锁的人是自己本身,那么仍可以获取到锁,并让“计时器”加1;
解锁的时候,当计时器为0时,这条线程才为解锁状态。
2.2.synchronized使用示例
基础用法:
Thread t1=new Thread(()->{for(int i=0;i<5000;i++){//使用synchronized锁住lock对象synchronized(lock){count++;}}});
变为方法使用:
//1.创建一个方法,在方法内调用synchronizedpublic static void add(){synchronized(lock){count++;}}//2.创建一个synchronized 类的方法synchronized public static void add(){count++;}
2.3.JAVA标准库中的线程安全类
JAVA库中的很多都是线程不安全的,他们都涉及数据的共享与修改,以下是一些线程安全的
- Vector
- HashTable
- ConcurrenHashMap
- StringBuffer
3.volatile关键字
3.1.volatile能保证内存可见性
实例:
package thread;
import java.util.Scanner;
//写两个线程,通过t2线程来改变t1线程
public class Demo27 {//定义f成员变量static int f=0;public static void main(String[] args) throws InterruptedException {Thread t1=new Thread(()->{//f=0时,while循环一直运行0while(f==0){//dosomthing}System.out.println("t1线程结束");});Thread t2=new Thread(()->{Scanner in = new Scanner(System.in);//改变f的值,让t1的while循环结束System.out.println("请输入一个数");f=in.nextInt();System.out.println("t2线程结束");});t1.start();t2.start();t1.join();t2.join();}
}
我们理想的结果应该是输入任意一个非0值,然后打印"t2线程结束""t1线程结束",可结果却是
只打印了 "t2线程结束",t1的线程还在运行,那么我们就可以通过该结果得出我们写的代码有一定的问题的
这就涉及到了”可见性“,在t2线程中修改的f变量,t1中没有发现
这其实也是编译器的优化,所谓的编译器优化是指如果大量读取一个相同的数据,那么接下来都会默认是该数据
首先每个JAVA进程都有一个”主内存“,而每个JAVA线程都有自己的”工作内存“,每个JAVA线程先从内存中读取数据到自己的工作内存,然后进行工作
所以上述代码中,t1线程将f=0读取到自己的工作内存(此时触发了编译器优化),然而t2修改的是主内存中的数据,t1的工作内存中的数据仍然是f=0,两者不会有影响。
这时候我们就需要使用volatile
public class Demo27 {//在f成员变脸添加volatilestatic volatile int f=0;public static void main(String[] args) throws InterruptedException {Thread t1=new Thread(()->{while(f==0){}System.out.println("t1线程结束");});Thread t2=new Thread(()->{Scanner in = new Scanner(System.in);System.out.println("请输入一个数");f=in.nextInt();System.out.println("t2线程结束");});t1.start();t2.start();t1.join();t2.join();}
}
结果:
- 代码运行时volatile从主内存读取最新的值返回给java线程的工作内存中
- volatile可以保证”内存可见性“
- 我们可以理解为使用volatile相当于告诉编译器让他停止优化功能
- 虽然运行速度慢了,但是数据更准确了
3.2.volatile不能保证原子性
- volatile和synchroized有本质区别
- volatile:保证”内存可见性“
- synchroized:保证”原子性“
4.wait和notify
由于线程之间是抢占式执行,所以线程的执行顺序是不稳定的,但我们需要使线程之间有序进行,所以就需要wait()和notify()两个方法。
4.1.wait()方法
功能:
- 让当前进程等待运行
- 释放当前的锁
- 当满足一定条件时被唤醒,并尝试重新获得锁
结束条件:
- 使用notify()唤醒
- wait自带的等待时间超时(wait中自带timeoutMillis,设置等待的时间)
实例:
package thread;
public class test20 {public static void main(String[] args) throws InterruptedException {//创建锁Object lock=new Object();//创建线程Thread t1=new Thread(()->{System.out.println("wait之前");//让线程等待//因为wait方法必须在同步代码块中使用//所以要先获取锁synchronized(lock){ try{lock.wait();}catch(InterruptedException e){e.printStackTrace();}}System.out.println("wait之后");});//启动线程t1.start();}
}
结果:
以上代码中运行后只打印了”wait之前“,我们再来看一下JAVA的管理控制台
可以看到我们创建的该线程的状态为等待状态
那么就说明了wait方法是将当前线程变为等待状态
4.2.notify()方法
功能:
- 唤醒一个正在等待的线程
- 只能唤醒一个等待的线程,如果由多个等待线程,notify则随机唤醒一个
- 在notify之后,不会马上释放锁,而是等待notify当前的同步代码块运行完才释放锁
实例1:
创建一个等待线程和一个唤醒线程
package thread;
public class test20 {public static void main(String[] args) throws InterruptedException {//创建锁Object lock=new Object();//创建线程--等待线程Thread t1=new Thread(()->{System.out.println("wait之前");//让线程等待//因为wait方法必须在同步代码块中使用//所以要先获取锁synchronized(lock){ try{lock.wait();}catch(InterruptedException e){e.printStackTrace();}}System.out.println("wait之后");});//创建线程--唤醒线程Thread t2=new Thread(()->{System.out.println("notify之前");//唤醒线程//因为notify方法必须在同步代码块中使用//所以要先获取锁synchronized(lock){lock.notify();}System.out.println("notify之后");});//启动线程t1.start();t2.start();}
}
结果:
实例2:
创建两个等待线程和一个唤醒线程
package thread;
public class text22 {public static void main(String[] args) throws InterruptedException {//创建锁Object lock=new Object();//创建线程--等待线程Thread t1=new Thread(()->{System.out.println("wait1之前");synchronized(lock){ try{lock.wait();}catch(InterruptedException e){e.printStackTrace();}}System.out.println("wait1之后");});Thread t2=new Thread(()->{System.out.println("wait2之前");synchronized(lock){ try{lock.wait();}catch(InterruptedException e){e.printStackTrace();}}System.out.println("wait2之后");});//创建线程--唤醒线程Thread t=new Thread(()->{System.out.println("notify之前");//唤醒线程//因为notify方法必须在同步代码块中使用//所以要先获取锁synchronized(lock){lock.notify();}System.out.println("notify之后");});//启动线程t1.start();t2.start();t.start();}}
结果:
运行后只唤醒了t1线程
所以notify只能唤醒一个线程
4.3.notifyAll ()方法
功能:
- 唤醒所有线程
实例:
创建三个等待线程和一个唤醒线程
package thread;public class text21 {public static void main(String[] args) throws InterruptedException {Object lock=new Object();Thread t1=new Thread(()->{System.out.println("wait1之前");synchronized (lock){try {lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("wait1之后");});Thread t2=new Thread(()->{System.out.println("wait2之前");synchronized(lock){try{lock.wait();}catch (InterruptedException e){throw new RuntimeException(e);}}System.out.println("wait2之前");});Thread t3=new Thread(()->{System.out.println("wait3之前");synchronized(lock){try{lock.wait();}catch(InterruptedException e){e.printStackTrace();}}System.out.println("wait3之前");});Thread t=new Thread(()->{System.out.println("notifyAll之前");synchronized(lock){lock.notifyAll();}System.out.println("notifyAll之前");});t1.start();t2.start();t3.start();t.start();}
}
结果:
可以看到所有等待线程都被唤醒,但是唤醒的顺序不同,因为是同时唤醒,notify一时间不知道先该唤醒谁,所以就导致了线程竞争。
4.4.notify 和 notifyAll的对比
可以想象notify/notifyAll和wait为老师和学生
notify老师检查背诵的时候,喜欢一个一个按号数叫wait学生起来背诵
notifyAll老师检查背诵的时候则是:“你们谁要来背诵,先背完的就可以走了”,那么好几个wait学生就争抢着取背诵~~
- notify为唤醒单个线程,有序
- notifyAll为同时唤醒多个线程,无序
4.5.wait 和 sleep 的对比
相同点:
- wait和sleep都可以使线程等待一段时间
- 都有超时时间。
不同点:
- wait是Object下的方法,sleep是Thread下的静态方法
- wait它的设计是为了提前唤醒,sleep它的设计是为了到点唤醒
- wait提前唤醒时不会报错,sleep提前唤醒时会报错
- wait需要上锁,sleep不需要上锁
相关文章:
JAVA EE_多线程-初阶(二)
1.线程安全 1.1.观察线程不安全 实例: package thread; public class text18 {//定义一个成员变量count,初始值为0private static int count0;public static void main(String[] args) throws InterruptedException {Thread t1new Thread(()->{for(int i0;i&l…...
【Linux】进程预备知识——冯诺依曼、操作系统
目录: 一、冯诺依曼体系结构 (一)什么是冯诺依曼 (二)为什么需要冯诺依曼 (三)冯诺依曼如何操作 二、操作系统概念 (一)对下硬件管理 (二)…...
Java入门首周精要:从基础语法到面向对象核心解析
文章目录 Java入门首周精要:从基础语法到面向对象核心解析1.Java类名与文件名的一致性规则2.Java和C语言中char类型的区别3.Java中的注释4.Java中的‘’‘’运算符5.Java的输入输出6.方法(重载&重写)方法的重载方法的重写 7.面向对象&…...
嵌入式AI开源生态指南:从框架到应用的全面解析
嵌入式AI开源生态指南:从框架到应用的全面解析 引言 随着人工智能技术的迅速发展,将AI能力部署到边缘设备上的需求日益增长。嵌入式AI通过在资源受限的微控制器上运行机器学习模型,实现了无需云连接的本地智能处理,大幅降低了延…...
MCP server的stdio和SSE分别是什么?
文章目录 一、Stdio:本地进程间通信的核心二、SSE:远程通信与实时推送的利器三、Stdio vs SSE:关键差异对比四、如何选择?场景驱动的决策指南五、实战建议与避坑指南实际操作结语在AI应用开发中,MCP(Model Context Protocol)协议正成为连接大模型与外部资源的核心桥梁。…...
哈希表(闭散列)的实现
目录 概念及定义 闭散列的介绍 闭散列底层实现 哈希表的定义 哈希表的构造 哈希表扩容 哈希表插入 哈希表删除 哈希表查找 概念及定义 哈希表,也成为散列表,在C中unordered_set和unordered_map的底层实现依靠的都是哈希表。 map和set的底层是红…...
Shiro学习(六):Shiro整合CAS实现单点登录
一、单点登录介绍 单点登录(Single Sign On),简称为 SSO,是比较流行的企业业务整合的解决方案之一。 SSO的定义是在多个[应用],用户只需要登录一次就可以访问所有相互信任的应用系统。 一般这种单点登录的实现方案&…...
HAProxy-ACL实战篇
HAProxy-ACL实战篇 IP说明172.25.254.101客户端172.25.254.102haproxy服务器172.25.254.103web1172.25.254.104web2 ACL示例-域名匹配 # 172.25.254.102 [rootRocky ~]# cat /etc/haproxy/conf.d/test.cfg frontend magedu_http_portbind 172.25.254.102:80mode httpbalanc…...
以下是针对该 Ansible 任务的格式检查和优化建议
以下是针对该 Ansible 任务的格式检查和优化建议: 目录 一、格式检查原始代码问题分析修正后的标准格式 二、推荐增强功能1. 添加可执行权限2. 显式指定 Shell 解释器3. 添加错误处理 三、完整 Playbook 示例四、验证脚本兼容性五、常见错误总结 一、格式检查 原始…...
C++语言的测试覆盖率
C语言的测试覆盖率分析与实践 引言 在软件开发过程中,测试覆盖率是一项重要的质量指标,它帮助开发者评估代码的测试效果,确保软件的可靠性与稳定性。尤其在C语言的开发中,由于其复杂的特性和丰富的功能,测试覆盖率的…...
如何使用 DrissionPage 进行网页自动化和爬取
在这个技术博客中,我们将向大家展示如何使用 DrissionPage 进行网页自动化操作与数据爬取。DrissionPage 是一个基于 Playwright 的 Python 自动化工具,它允许我们轻松地控制浏览器进行网页爬取、测试以及自动化操作。与其他工具(如 Selenium…...
设计模式 Day 3:抽象工厂模式(Abstract Factory Pattern)详解
经过前两天的学习,我们已经掌握了单例模式与工厂方法模式,理解了如何控制实例个数与如何通过子类封装对象的创建逻辑。 今天,我们将进一步深入“工厂”体系,学习抽象工厂模式(Abstract Factory Pattern)&a…...
TensorRT 有什么特殊之处
一、TensorRT的定义与核心功能 TensorRT是NVIDIA推出的高性能深度学习推理优化器和运行时库,专注于将训练好的模型在GPU上实现低延迟、高吞吐量的部署。其主要功能包括: 模型优化:通过算子融合(合并网络层)、消除冗余…...
SQL注入-盲注靶场实战(手写盲注payload)--SRC获得库名即可
布尔盲注 进入页面 注入点 ’ and 11 and 12 得知为布尔盲注 库名长度 and length(database()) 8 抓包(浏览器自动进行了url编码)爆破 得知为 12 库名字符 1 and ascii(substr(database(),1,1))112 – q (这里如果不再次抓包…...
http://noi.openjudge.cn/_2.5基本算法之搜索_1804:小游戏
文章目录 题目深搜代码宽搜代码深搜数据演示图总结 题目 1804:小游戏 总时间限制: 1000ms 内存限制: 65536kB 描述 一天早上,你起床的时候想:“我编程序这么牛,为什么不能靠这个赚点小钱呢?”因此你决定编写一个小游戏。 游戏在一…...
Windows Flip PDF Plus Corporate PDF翻页工具
软件介绍 Flip PDF Plus Corporate是一款功能强大的PDF翻页工具,也被称为名编辑电子杂志大师。这款软件能够迅速将PDF文件转换为具有翻页动画效果的电子书,同时保留原始的超链接和书签。无论是相册、视频、音频,还是Flash、视频和链接&#…...
Java八股文-List
集合的底层是否加锁也就代表是否线程安全 (一)List集合 一、数组 array[1]是如何通过索引找到堆内存中对应的这块数据的呢? (1)数组如何获取其他元素的地址值 (2)为什么数组的索引是从0开始的,不可以从1开始吗 (3)操作数组的时间复杂度 ①查找 根据索引查询 未…...
btrfs , ext4 , jfs , xfs , zfs 对比 笔记250406
btrfs , ext4 , jfs , xfs , zfs 对比 笔记250406 特性Btrfsext4JFSXFSZFS定位现代多功能传统稳定轻量级高性能大文件企业级存储最大文件/分区16EB / 16EB16TB / 1EB4PB / 32PB8EB / 8EB16EB / 25610⁵ ZB快照✅ 支持❌ 不支持❌ 不支持❌ 不支持✅ 支持透明压缩✅ (Zstd/LZO)❌…...
Meta上新Llama 4,到底行不行?
这周AI圈被Meta的“深夜突袭”炸开了锅。 Llama 4家族带着三个新成员,直接杀回开源模型战场,连扎克伯格都亲自站台喊话:“我们要让全世界用上最好的AI!” 但别急着喊“王炸”,先看看它到底强在哪。 这次Meta玩了个狠招…...
显示器工艺简介
华星光电显示器的生产工艺流程介绍,从入厂原料到生产出显示器的整体工艺介绍 华星光电显示器的生产工艺流程主要包括以下几个阶段,从原材料入厂到最终显示器的生产: 原材料准备 玻璃基板:显示器的核心材料,通常采用超…...
音乐软件Pro版!内置音源,听歌自由,一键畅享!
今天给大家介绍一款超实用的音乐软件——LX音乐Pro版。原版LX音乐需要用户自行导入音源才能正常使用,但此次推出的Pro版已经内置了音源,省去了繁琐的操作步骤,使用起来更加便捷 这款软件不仅支持歌曲搜索,还能搜索歌单,…...
Spring 中有哪些设计模式?
🧠 一、Spring 中常见的设计模式 设计模式类型Spring 中的应用场景单例模式创建型默认 Bean 是单例的工厂模式创建型BeanFactory、FactoryBean抽象工厂模式创建型ApplicationContext 提供多个工厂接口代理模式结构型AOP 动态代理(JDK/CGLIB)…...
R语言使用ggplot2作图
在ggplot2中,图是采用串联起来()号函数创建的。每个函数修改属于自己的部分。比如,ggplot()geom()...... aes(x, y, colour a,shape a,size a.......) ggplot2中画图常用的五大块内容 数据(data)及一系列将数据中的变量对应到图…...
GenerationMixin概述
类 类名简单说明GenerateDecoderOnlyOutput继承自 ModelOutput,适用于非束搜索方法的解码器-only模型输出类。GenerateEncoderDecoderOutput继承自 ModelOutput,适用于非束搜索方法的编码器-解码器模型输出类。GenerateBeamDecoderOnlyOutput继承自 Mod…...
文心快码制作微信小程序
AI时代来临,听说Baidu Comate也推出了自家的编程工具Zulu,可以从零到一帮你生成代码,趁着现在还免费,试试效果如何。这里以开发一个敲木鱼的微信小程序为例 一、需求分析 写小程序需求文档 首先,第一步我要准确描述…...
flutter provider状态管理使用
在 Flutter 中,Provider 是一个轻量级且易于使用的状态管理工具,它基于 InheritedWidget,并提供了一种高效的方式来管理和共享应用中的状态。相比其他复杂的状态管理方案(如 Bloc 或 Riverpod),Provider 更…...
C++——静态成员
目录 静态成员的定义 静态成员变量 编程示例 存在的意义 静态成员函数 类内声明 类外定义 编程示例 静态成员的定义 静态成员在C类中是一个重要的概念,它包括静态成员变量和静态成员函数。静态成员的特点和存在的意义如下: 静态成员变量 1…...
UDP学习笔记(四)UDP 为什么大小不能超过 64KB?
🌐 UDP 为什么大小不能超过 64KB?TCP 有这个限制吗? 在进行网络编程或者调试网络协议时,我们常常会看到一个说法: “UDP 最大只能发送 64KB 数据。” 这到底是怎么回事?这 64KB 是怎么来的?TCP…...
Linux中用gdb查看coredump文件
查看dump的命令: gdb 可执行文件 dump文件路径查看函数调用栈 (gdb)bt查看反汇编代码 (gdb)disassemble查看寄存器的值 (gdb)info all-registers如果通过上述简单命令无法排查,还是通过-g参数编译带符号表的可执行文件,再用gdb查看...
PyTorch 深度学习 || 7. Unet | Ch7.1 Unet 框架
1. Unet 框架...
LeetCode 热题 100 堆
215. 数组中的第K个最大元素 给定整数数组 nums 和整数 k,请返回数组中第 **k** 个最大的元素。 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 …...
LeetCode栈 155. 最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。void push(int val) 将元素val推入堆栈。void pop() 删除堆栈顶部的元素。int top() 获取堆栈顶部的元素。int get…...
Linux系统03---文件操作时间编程
目录 文件操作 1.1 缓冲区 1.2 基于缓冲区的文件操作---高级 IO 1.3 基于非缓冲区的文件操作—低级 IO 1.3.1 文件描述符 int fd; 1.3.2 函数名:open() 1.3.3 函数名:close() 1.3.4 函数名:write() 1.3.5 函数名:read(…...
4月5日作业
需求: 1.按照图示的VLAN及IP地址需求,完成相关配置 2.要求SW 1为VLAN 2/3的主根及主网关 SW2为VLAN 20/30的主根及主网关,SW1和 SW2互为备份 3.可以使用super vlan 4.上层通过静态路由协议完成数据通信过程 5.AR1为企业出口路由器…...
新一代AI架构实践:数字大脑AI+智能调度MCP+领域执行APP的黄金金字塔体系
新一代AI架构实践:数字大脑智能调度领域执行的黄金金字塔体系 一、架构本质的三层穿透性认知 1.1 核心范式转变(CPS理论升级) 传统算法架构:数据驱动 → 特征工程 → 模型训练 → 业务应用 新一代AI架构:物理规律建…...
低代码开发:重塑软件开发的未来
在数字化转型的浪潮中,企业对软件开发的需求呈爆炸式增长。然而,传统软件开发模式面临着开发周期长、成本高、技术门槛高等诸多挑战。低代码开发平台(Low-Code Development Platform)应运而生,它通过可视化编程和拖拽式…...
小型园区网实验作业
拓扑搭建: 实验需求: 1、按照图示的VLAN及IP地址需求,完成相关配置 2、要求SW1为VLAN 2/3的主根及网关 SW2 为VLAN 20/30 的主根及主网关 SW1和SW2互为备份 3、可以使用super vlan 4、上层通过静态路由协议完成数据通信过程 5、A…...
Gateway 网关 快速开始
一、核心概念 路由(route) 路由是网关中最基础的部分,路由信息包括一个ID、一个目的URI、一组断言工厂、一组Filter组成。如果断言为真,则说明请求的 URL 和配置的路由匹配。 断言(predicates) 断言函数允许开发者去定义匹配 Http Request 中…...
C++中如何使用STL中的list定义一个双向链表,并且实现增、删、改、查操作
一、STL中的 list 是双向链表,但不是循环链表,通过指针访问结点数据,它的内存空间可以是不连续的,使用它能高效地进行各种操作。 二、代码 #include <bits/stdc.h> using namespace std;// 打印链表元素的函数 void print…...
shell脚本中捕获键盘中断信号trap
在 Shell 脚本中,可以通过 trap 命令捕获键盘中断信号(通常是 SIGINT,即 CtrlC)。以下是具体的实现方法: 1.使用 trap 捕获键盘中断信号 trap 命令用于捕获信号并执行相应的命令或函数。SIGINT(信号编号为 …...
让ChatGPT用DeepReaserch指导进行学术写作
目录 ChatGPT在学术论文写作中的作用与分阶段提示词指南 1.选题阶段(确定研究课题方向) 2.文献综述阶段(调研与综述已有研究) 3.研究设计阶段(设计研究方法与框架) 4.撰写正文阶段(撰写各部…...
Compose笔记(十四)--LazyColumn
这一节了解一下Compose中的LazyColumn,在Jetpack Compose 中,LazyColumn 是一个用于高效显示长列表或可滚动垂直布局的组件。它类似于传统 Android 开发中的 RecyclerView,但专为 Compose 的声明式 UI 框架设计,能够显著优化性能&…...
CNN-SE-Attention-ITCN多特征输入回归预测(Matlab完整源码和数据)
CNN-SE-Attention-ITCN多特征输入回归预测(Matlab完整源码和数据) 目录 CNN-SE-Attention-ITCN多特征输入回归预测(Matlab完整源码和数据)预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.一种适合光伏功率回归预测的高创…...
Spring Data JPA中的List底层:深入解析ArrayList的奥秘!!!
🌟 Spring Data JPA中的List底层:深入解析ArrayList的奥秘 💡 你是否好奇过,为什么Spring Data JPA的查询方法返回的List<T>总是默认为ArrayList?本文将通过技术原理解析、验证实验和性能优化指南,为…...
redis高并发缓存架构与性能优化
Redlock实现原理 超过半数redis节点加锁成功才算成功加锁。 Redlock存在问题 如果主节点挂掉,还没有同步到从节点,重新选举出主节点,那加锁就没有加到这个新的主节点上。 如果增加redis主节点数,那么加锁的性能更差,要…...
解锁多邻国:全方位语言学习新体验
解锁多邻国:全方位语言学习新体验 在数字化学习浪潮中,多邻国(Duolingo)凭借独特优势,成为全球超 5 亿用户的语言学习首选。这款 2012 年诞生于美国匹兹堡的应用,2019 年进入中国市场后,…...
Docker部署SeraXNG接入dify报错解决
报错: 设置授权 配置凭据后,工作区中的所有成员都可以在编排应用程序时使用此工具。 SearXNG base URL* 如何获取 PluginInvokeError: {"args":{},"error_type":"ToolProviderCredentialValidationError","message&q…...
Zookeeper的作用详解
Zookeeper作为分布式协调服务,在分布式系统中承担核心协调角色,其作用可归纳为以下核心功能模块: 一、分布式协调与同步 分布式锁管理 提供独占锁和共享锁,通过创建临时顺序节点实现锁的公平竞争。例如,客户端在/distr…...
高频面试题(含笔试高频算法整理)基本总结回顾34
干货分享,感谢您的阅读! (暂存篇---后续会删除,完整版和持续更新见高频面试题基本总结回顾(含笔试高频算法整理)) 备注:引用请标注出处,同时存在的问题请在相关博客留言…...
Dify 与 n8n 对比分析:AI 应用开发与自动化工作流工具的深度比较
Dify 与 n8n 对比分析:AI 应用开发与自动化工作流工具的深度比较 摘要 本文对比分析了 Dify 和 n8n 两款工具的核心定位、功能特点、适用场景及技术门槛。Dify 专注于 AI 应用开发,适合快速搭建智能客服、知识库检索等场景;n8n 则定位于通用…...