Java【多线程】(2)线程属性与线程安全
目录
1.前言
2.正文
2.1线程的进阶实现
2.2线程的核心属性
2.3线程安全
2.3.1线程安全问题的原因
2.3.2加锁和互斥
2.3.3可重入(如何自己实现可重入锁)
2.4.4死锁(三种情况)
2.4.4.1第一种情况
2.4.4.2第二种情况
2.4.4.3第三种情况
2.4.5避免死锁
3.小结
1.前言
哈喽大家好吖,今天继续来给大家分享线程相关的内容,介绍一部分线程的核心属性,后一部分主要为线程安全部分,当然一篇博文无法讲解完全,会在后续接着为大家讲解。
2.正文
2.1线程的进阶实现
上一篇关于线程的博文我们通过Thread类或实现Runnable接口来达到了多线程的实现,接下来给大家一个最推荐的实现方式:lambda表达式实现。
Thread
类的构造函数接受一个Runnable
接口类型的参数,而Runnable
接口有一个run
方法。因此,我们可以通过lambda表达式来实现这个接口,并将其传递给Thread
构造器。public class test {public static void main(String[] args) {// 使用lambda表达式创建线程Thread thread = new Thread(() -> {// 线程执行的代码for (int i = 0; i < 5; i++) {System.out.println("线程正在运行: " + i);try {Thread.sleep(1000); // 模拟线程工作1秒} catch (InterruptedException e) {e.printStackTrace();}}});thread.start(); // 启动线程} }
详解:
Runnable
接口:Runnable
接口包含一个run
方法,定义了线程要执行的任务。- Lambda表达式:
()->{}
部分是Lambda表达式,它实现了Runnable
接口的run
方法。这个方法中包含了线程要执行的代码。Thread
对象:使用Thread
类创建一个新线程,并传入Runnable
的实现(即Lambda表达式)。thread.start()
:调用start()
方法来启动线程。线程开始执行Lambda表达式中的run
方法。
2.2线程的核心属性
线程有不同的生命周期状态,主要包括以下几种:
- NEW:线程被创建,但还未启动。
- RUNNABLE:线程正在执行或等待操作系统分配CPU时间片。就绪状态分为俩种:
- 随时可以到cpu上去工作。
- 在cpu上正在工作。
- BLOCKED:线程因为竞争资源(如同步锁)而被阻塞,无法执行。
- WAITING:线程正在等待另一个线程的通知。
- TIMED_WAITING:线程正在等待一个特定的时间段,直到超时或被唤醒。(例如线程的join方法会使线程进入此状态)
- TERMINATED:线程执行完毕,已终止。
附上别的大佬总结很详细的图片。
2.3线程安全
再将这个板块之前,先给大家一个案例来引入线程安全这个概念。我们当下有这么一个场景:
public class demo2 {public static int count = 0;public static void main(String[] args) {Thread t1 = new Thread(()->{for (int i = 0;i < 500;i++){count++;}});Thread t2 = new Thread(()->{for (int i = 0;i < 500;i++){count++;}});t1.start();t2.start();System.out.println(count);}
}
我们可以看到,我们希望通过俩个线程来完成count自增到1000的操作,打没输出结果并不是我们想要的。
原因是线程刚启动,可能还没有分配到cpu上开始执行,count便被打印出来。
我们这样处理后:
public class demo2 {public static int count = 0;public static void main(String[] args) {Thread t1 = new Thread(()->{for (int i = 0;i < 500;i++){count++;}});Thread t2 = new Thread(()->{for (int i = 0;i < 500;i++){count++;}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(count);}
}
发现可以出来希望的结果:
那如果我们只让一个线程加上join呢?会发现结果开始变得随机起来:
因此我们可以知道,上述有线程产生的“bug”即没有输出想要的结果,就被称为线程安全问题,相反,如果在多线程并发的情况下,输出理想结果就叫做“线程安全”。
2.3.1线程安全问题的原因
- 【根因】随即调度,抢占执行(上文例子就是如此)
- 多个线程同时修改一个变量
- 修改操作不是原子性的(意思是某些操作如count++是由多个线程组成完成的)
- 内存可见性(意思是某些变量的访问不一定直接访问到内存,而是有可能访问到寄存器当中)
- 不当锁的使用(下文细讲)
2.3.2加锁和互斥
如何处理这些线程安全问题呢,这里我们要引入加锁的概念与synchronized关键字。
加锁是一种同步机制,用于控制多个线程访问共享资源的顺序。
当一个线程获得了锁时,其它线程必须等待该线程释放锁后才能继续访问共享资源。加锁的特点:
- 串行化访问:
- 同一时刻只有一个线程可以访问被加锁的资源。
- 防止数据竞争:
- 确保共享资源的操作是原子性的(不会被其他线程中断)。
- 提升数据一致性:
- 确保共享资源不会因为多个线程同时操作而引发不一致问题。
加锁的过程:
- 加锁(Locking): 一个线程试图获取资源的锁,若获取成功,进入临界区;若失败,则阻塞或等待。
- 解锁(Unlocking): 线程释放锁,允许其他线程获取锁并继续执行
互斥(Mutual Exclusion,缩写为 Mutex)是加锁的目的之一,强调同一时刻只能有一个线程访问某个共享资源,达到线程之间的互斥访问。
如何实现加锁呢,继续拿上文来举例子:
public class demo2 {private int count = 0;// 同步实例方法public synchronized void increment() {count++;}public int getCount() {return count;}public static void main(String[] args) {demo2 demo = new demo2();Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {demo.increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {demo.increment();}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Final count: " + demo.getCount());} }
运行结果:
2.3.3可重入(如何自己实现可重入锁)
什么叫可重入呢,我们用一段代码来引入这个概念:
class Counter {private int count = 0;public void add() {synchronized (this) {count++;//第一次加锁}}public int get() {return count;}
}
public class demo3 {public static void main(String[] args) {Counter counter = new Counter();Thread t1 = new Thread(()->{for(int i = 0;i < 100;i++){synchronized (counter){counter.add();//第二次加锁}}});t1.start();try {t1.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("count = " + counter.get());}
}
上面代码我们可以看到(如果没有可重入这个概念):
- 第一次加锁操作,能够成功(锁没人使用)。
- 第二次进行加锁,此时意味着,锁对象已经是被占用的状态,第二次加锁就会出现阻塞等待。
要想解除阻塞,只能往下执行才可以,要想往下执行,就需要等到第一次锁被释放,这样就叫做出现了死锁。
为了解决上述问题,Java中的synchronized引入了可重入的概念:
可重入锁是一种允许同一线程多次获取同一把锁的同步机制,解决了嵌套调用或递归场景下线程自我阻塞的问题,是避免死锁的重要设计。
所以多个锁递归,只有最外层的锁涉及真正的加锁与解锁。
那我们如何自己实现一个可重入锁呢,抓住下面核心就有头绪了:
可重入锁的核心机制
锁计数器:
每个锁对象内部维护一个计数器,记录被同一线程获取的次数。
首次获取锁时计数器=1,每次重入加1,释放时减1,归零后其他线程可竞争锁。
持有线程标识:
锁对象记录当前持有锁的线程,确保仅持有线程可重入。
下面附上示例:
public class MyLock {private Thread ownerThread; // 当前持有锁的线程private int lockCount = 0; // 锁计数器// 获取锁public synchronized void lock() throws InterruptedException {Thread currentThread = Thread.currentThread();// 若锁已被其他线程持有,则当前线程等待while (ownerThread != null && ownerThread != currentThread) {wait();}// 锁未被持有或当前线程重入,更新计数器和持有线程ownerThread = currentThread;lockCount++;}// 释放锁public synchronized void unlock() {Thread currentThread = Thread.currentThread();// 只有持有锁的线程可以释放锁if (ownerThread != currentThread) {throw new IllegalMonitorStateException("当前线程未持有锁!");}lockCount--;// 锁计数器归零时完全释放锁if (lockCount == 0) {ownerThread = null;notify(); // 唤醒一个等待线程}}
}
2.4.4死锁(三种情况)
2.4.4.1第一种情况
一个线程,一个锁,被加锁多次。想必这个上文刚讲过,就不多言了,着重讲后文。
2.4.4.2第二种情况
两个线程,两个锁,互相尝试获得对方的锁。可能直接这样讲不是很好懂,附上代码与注释就可以了:
public class Demo20 {public static void main(String[] args) throws InterruptedException {// 创建两个锁对象,用于线程同步Object locker1 = new Object();Object locker2 = new Object();// 创建线程 t1Thread t1 = new Thread(() -> {// 获取 locker1 的锁synchronized (locker1) {try {// 线程休眠 1 秒,模拟耗时操作Thread.sleep(1000);} catch (InterruptedException e) {// 如果线程被中断,抛出异常throw new RuntimeException(e);}// 尝试获取 locker2 的锁synchronized (locker2) {// 如果成功获取到 locker2 的锁,打印消息System.out.println("t1 线程两个锁都获取到");}}});// 创建线程 t2Thread t2 = new Thread(() -> {// 获取 locker1 的锁synchronized (locker1) {try {// 线程休眠 1 秒,模拟耗时操作Thread.sleep(1000);} catch (InterruptedException e) {// 如果线程被中断,抛出异常throw new RuntimeException(e);}// 尝试获取 locker2 的锁synchronized (locker2) {// 如果成功获取到 locker2 的锁,打印消息System.out.println("t2 线程两个锁都获取到");}}});// 启动线程 t1 和 t2t1.start();t2.start();// 主线程等待 t1 和 t2 执行完毕t1.join();t2.join();}
}
线程 t1:
先获取
locker1
的锁,然后休眠 1 秒。接着尝试获取
locker2
的锁。线程 t2:
同样先获取
locker1
的锁,然后休眠 1 秒。接着尝试获取
locker2
的锁。问题:此时死锁就出现了
t1
持有locker1
并等待locker2
。
t2
持有locker1
并等待locker2
。两个线程互相等待对方释放锁,导致程序无法继续执行。
2.4.4.3第三种情况
死锁的第三种情况,即n个线程和m把锁,这里就要引入一个很著名的问题,哲学家就餐问题:
哲学家就餐问题(Dining Philosophers Problem) 是计算机科学中经典的同步与死锁问题,由 Edsger Dijkstra 提出,用于演示多线程环境中的资源竞争和死锁风险。
1. 问题描述
场景:5 位哲学家围坐在圆桌旁,每人面前有一碗饭,相邻两人之间放一支筷子(共 5 支筷子)。
行为:
哲学家交替进行 思考 和 就餐。
就餐时需要 同时拿起左右两边的筷子。
完成就餐后放下筷子,继续思考。
核心问题:如何设计算法,使得所有哲学家都能公平、高效地就餐,且避免死锁。
2. 死锁的产生
如果所有哲学家 同时拿起左边的筷子,会发生以下情况:
每个哲学家都持有左边的筷子,等待右边的筷子。
右边的筷子被其他哲学家持有,形成 循环等待。
所有哲学家无法继续,导致 死锁。
3. 解决思路
核心思想:为所有资源(筷子)定义一个全局顺序,要求哲学家必须按固定顺序获取资源。
实现方式:
将筷子编号为 0 到 4。
每位哲学家必须先拿编号较小的筷子,再拿编号较大的筷子。
效果:
破坏循环等待条件(不可能所有人同时等待右侧筷子)。
保证至少一位哲学家可以拿到两只筷子。
2.4.5避免死锁
上述讲完了死锁出现的场景,这里可以总结死锁出现的四个必要条件:
- 锁是互斥的。(一个线程拿到锁之后,另一个线程再尝试获取锁,必须要阻塞等待)
- 锁是不可抢占的。(即线程1拿到锁, 线程2也尝试获取这个锁,线程2 必须阻塞等待2而不是线程2直接把锁抢过来)
- 请求和保持。(一个线程拿到锁1之后,不释放锁1 的前提下,获取锁2)
- 循环等待。(多个线程, 多把锁之间的等待过程,构成了"循环",即A 等待 B, B 也等待 A 或者 A 等待 B,B 等待 C,C等待 A)
既然我们知道死锁是如何产生的,那么解决死锁的思路就有啦:
- 打破3条件,可以把嵌套的锁改成并列的锁。
- 打破4条件,加锁的顺序进行约定。
3.小结
今天的分享到这里就结束了,喜欢的小伙伴不要忘记点点赞点个关注,你的鼓励就是对我最大的支持,加油!
相关文章:
Java【多线程】(2)线程属性与线程安全
目录 1.前言 2.正文 2.1线程的进阶实现 2.2线程的核心属性 2.3线程安全 2.3.1线程安全问题的原因 2.3.2加锁和互斥 2.3.3可重入(如何自己实现可重入锁) 2.4.4死锁(三种情况) 2.4.4.1第一种情况 2.4.4.2第二种情况 2.4…...
浅克隆与深克隆区别
package d12_api_object;public class Test2 {public static void main(String[] args) throws CloneNotSupportedException {//目标:掌握Object类提供的对象克隆方法//1、protected Object clone():对象克隆User u1 new User(1,"min","1120",…...
【计算机网络入门】初学计算机网络(九)
目录 1.令牌传递协议 2. 局域网&IEEE802 2.1 局域网基本概念和体系结构 3. 以太网&IEEE802.3 3.1 MAC层标准 3.1.1 以太网V2标准 编辑 3.2 单播广播 3.3 冲突域广播域 4. 虚拟局域网VLAN 1.令牌传递协议 先回顾一下令牌环网技术,多个主机形成…...
数列极限入门习题
数列极限入门习题 lim n → ∞ ( 1 1 2 1 3 ⋯ 1 n ) 1 n \lim\limits_{n\rightarrow\infty}(1 \frac{1}{2}\frac{1}{3}\cdots\frac{1}{n})^{\frac{1}{n}} n→∞lim(12131⋯n1)n1 lim n → ∞ ( 1 n 1 1 n 2 ⋯ 1 n n ) \lim\limits_{n\rightarrow\…...
【决策树】分类属性的选择
文章目录 1.信息增益(ID3)2.信息增益率(C4.5)3.基尼指数(CART)ps.三者对比 实现决策树算法最关键的一点就是如何从所有的特征属性中选择一个最优的属性对样本进行分类,这种最优可以理解为希望划…...
Mysql面试篇笔记:
优化: 1.如何定位慢查询: 首先压测接口,查看那个接口比较慢,可以通过多种工具,比如Skywaking 可以查看各个接口响应时间,查看接口最慢,然后去跟踪接口,查看详细信息&#…...
005-Docker 安装 Redis
Docker 安装 Redis 1.从镜像官网拉取Redis镜像2.创建实例并启动3.测试连接4.设置开机启动 1.从镜像官网拉取Redis镜像 镜像官网地址:https://hub.docker.com执行命令 -- 拉取最新的版本 docker pull redis查看镜像 docker images2.创建实例并启动 先创建好需要的…...
可终身授权的外国工具,不限次数使用!PDF转CAD的软件
最近有不少朋友问我有没有好用的CAD转换工具,今天就来给大家分享两款超实用的小软件,希望能帮到大家。 第一款软件是一款国外开发的,它专门用来把PDF文件转换成CAD格式,特别方便。 这款软件的操作非常简单,打开后无需安…...
GaussDB性能调优技术指南
一、性能调优核心目标 降低响应时间:缩短单次查询或事务的处理时间(如从秒级优化到毫秒级)。 提高吞吐量:支撑更高并发请求(如从千次/秒提升到百万次/秒)。 资源高效利用:减少 CPU、…...
iOS逆向工程专栏 第13篇:iOS动态分析基础
iOS逆向工程专栏 第13篇:iOS动态分析基础 引言 在前面的文章中,我们详细探讨了iOS系统架构、逆向开发环境搭建、Mach-O文件格式分析,以及各种静态分析工具和技术。通过静态分析,我们可以了解应用的结构、类和方法定义,以及基本的控制流程。然而,静态分析也存在明显的局…...
【现代深度学习技术】卷积神经网络03:填充和步幅
【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈PyTorch深度学习 ⌋ ⌋ ⌋ 深度学习 (DL, Deep Learning) 特指基于深层神经网络模型和方法的机器学习。它是在统计机器学习、人工神经网络等算法模型基础上,结合当代大数据和大算力的发展而发展出来的。深度学习最重…...
(链表 删除链表的倒数第N个结点)leetcode 19
设空结点指向head便于插入和删除结点 考虑特殊情况 head结点被删除 a结点仅用来测试长度,找到目标结点的位置 b结点为空结点指向head返回值 cur用来删除目标值(特殊情况 目标值为head 这时curb) 则开始就将cur初始化为b开始遍历 /*** Definition fo…...
初阶数据结构(C语言实现)——3顺序表和链表(2)
2.3 数组相关面试题 原地移除数组中所有的元素val,要求时间复杂度为O(N),空间复杂度为O(1)。OJ链接 力扣OJ链接-移除元素删除排序数组中的重复项。力扣OJ链接-删除有序数组中的重复项合并两个有序数组。力扣OJ链接-合并两个有序数组 2.3.1 移除元素 1…...
leetcode 138. 随机链表的复制
题目如下 数据范围 这道题十分好,一定要自己写看看再来看别人的答案! 首先复制题目给出的链表,对于每个新生成的node利用名为ri的map记录它们在链表的位置和指针。 接着利用名为rd的map存储每个链表中random对应的位置比如(0,…...
【OpenCV C++】以时间命名存图,自动检查存储目录,若不存在自动创建, 按下空格、回车、Q、S自动存图
文章目录 // 保存图像的函数 void saveImage(const cv::Mat& frame) {// 生成唯一文件名auto now = std::chrono::system_clock::...
C# OnnxRuntime部署DAMO-YOLO人头检测
目录 说明 效果 模型信息 项目 代码 下载 参考 说明 效果 模型信息 Model Properties ------------------------- --------------------------------------------------------------- Inputs ------------------------- name:input tensor:Floa…...
DDD该怎么去落地实现(4)多对多关系
多对多关系的设计实现 如题,DDD该如何落地呢?前面我通过三期的内容,讲解了DDD落地的关键在于“关系”,也就是通过前面我们对业务的理解先形成领域模型,然后将领域模型的原貌,形成程序代码中的服务、实体、…...
Vue 3 组件库开发实战:打造基础 UI 组件库并发布 - 构建可复用的 Vue 组件资产
引言 欢迎再次回到 Vue 3 + 现代前端工程化 系列技术博客! 在昨天的第六篇博客中,我们深入探索了 Vue 3 Composition API 的进阶应用,通过构建可拖拽看板应用,熟练掌握了自定义 Hook 的代码复用技巧。 今天,我们将迈向 Vue 3 组件化开发的更高阶段,聚焦于 组件库的开发与…...
UNION 和 UNION ALL 的区别:深入解析 SQL 中的合并操作
在 SQL 的世界里,当我们需要合并多个查询结果集时,UNION和UNION ALL是两个常用的操作符。虽然它们的功能看起来相似,但实际上有着重要的区别,这些区别在不同的应用场景中会对查询结果和性能产生显著影响。本文将详细探讨UNION和UN…...
Redis 哈希(Hash)
Redis 哈希(Hash) 概述 Redis 哈希(Hash)是一种特殊的键值对类型,它允许存储结构化的数据,例如一个对象或记录。每个哈希值可以包含多个字段,每个字段又可以存储一个字符串值。这使得Redis哈希非常适合用于存储对象的…...
Android Activity栈关系解析
在 Android 系统中,这些类共同构成了 Activity 任务栈管理的核心架构。它们的关系可以类比为一栋大楼的管理体系,每个类负责不同层级的任务。以下是它们的详细解释和实际场景示例: 1. ActivityRecord(活动记录) 是什么…...
7.1.2 计算机网络的分类
文章目录 分布范围交换方式 分布范围 计算机网络按照分布范围可分为局域网、广域网、城域网。局域网的范围在10m~1km,例如校园网,网速高,主要用于共享网络资源,拓扑结构简单,约束少。广域网的范围在100km,例…...
Arcgis中添加脚本工具箱
准备资料 (1)工具箱 (2)python脚本 1、打开arcmap 2、找到目录窗口 3、复制粘贴工具箱的路径 4、添加或者确认python脚本路径 脚本上右键属性(注意:脚本内容和路径最后都不要有中文,否则可能报错) 如果…...
【Python 数据结构 1.零基础复习】
目录 一、输入与输出 1.输入 2.格式化输出 二、数字与变量 1.字符串 & 整型 2.字符串 & 整型 & 浮点型 3.变量 练习 2235. 两整数相加 三、运算与操作 1.四则运算 练习 2769. 找出最大的可达成数字 3.取整与取余 练习 2651. 计算列车到站时间 编辑 四、真与假 1…...
颠覆NLP的魔法:深度解读Transformer架构及其核心组件
目录 颠覆NLP的魔法:深度解读Transformer架构及其核心组件 一、Transformer 架构概述 二、核心组件解析 1. Self-Attention(自注意力机制) 2. 位置编码(Positional Encoding) 3. 多头注意力(Multi-Hea…...
【pytest框架源码分析二】pluggy源码分析之add_hookspecs和register
这里我们看一下_manager.py里的类和方法,最主要的是PluginManager类,类的初始化函数如下: class PluginManager:"""Core class which manages registration of plugin objects and 1:N hookcalling.You can register new hoo…...
【leetcode hot 100 53】最大子数组和
解法一:(动态规划)我们用 f(i) 代表以第 i 个数结尾的「连续子数组的最大和」,那么很显然我们要求的答案就是:max{f(i)},f(i)max{f(i−1)nums[i],nums[i]} class Solution {public int maxSubArray(int[] …...
Sqlserver安全篇之_启用TLS即配置SQL Server 数据库引擎以加密连接
官方文档 https://learn.microsoft.com/zh-cn/sql/database-engine/configure-windows/configure-sql-server-encryption?viewsql-server-ver16 https://learn.microsoft.com/zh-cn/sql/database-engine/configure-windows/manage-certificates?viewsql-server-ver15&pre…...
009---基于Verilog HDL的单比特信号边沿检测
文章目录 摘要一、边沿检测二、时序逻辑实现2.1 rtl2.2 tb 三、组合逻辑实现3.1 rtl3.2 tb 摘要 文章为学习记录。采用时序逻辑和组合逻辑实现边沿检测的核心逻辑。组合逻辑实现的上升沿和下降沿的脉冲比时序逻辑实现的上升沿和下降沿的脉冲提前一拍。 一、边沿检测 边沿检测…...
istio的核心概念简介
Istio 是一个开源的服务网格(Service Mesh)平台,旨在帮助管理、连接、保护和观察分布式微服务架构中的服务。它最初由 Google、IBM 和 Lyft 合作开发,广泛应用于 Kubernetes 环境。Istio 的核心目标是通过提供统一的流量管理、安全…...
如何在Apple不再支持的MacOS上安装Homebrew
手头有一台2012年产的Macbook Pro,系统版本停留在了10.15.7(2020年9月24日发布的)。MacOS 11及后续的版本都无法安装到这台老旧的电脑上。想通过pkg安装Homebrew,发现Homebrew releases里最新的pkg安装包不支持MacOS 10.15.7&…...
@update 的常见用法 Vue.js
在 Vue.js 中,update 是一个事件监听器,通常用于监听自定义组件或某些 Vue 原生组件(如 <input> 或自定义组件)的更新事件。它并不是 Vue 的核心 API,而是一种约定俗成的命名方式,用于处理组件内部状…...
C#开发——日期操作类DateTime
在C#中,日期和时间的操作主要通过 System.DateTime 类来实现。 DateTime 提供了丰富的属性和法,用于处理日期和时间的创建、格式化、比较和计算等操作。以下是一些常用的日期函数和特性: 一、创建日期和时间 1、直接指定日期和时间&…...
大语言模型学习--LangChain
LangChain基本概念 ReAct学习资料 https://zhuanlan.zhihu.com/p/660951271 LangChain官网地址 Introduction | 🦜️🔗 LangChain LangChain是一个基于语言模型开发应用程序的框架。它可以实现以下应用程序: 数据感知:将语言模型…...
Oracle数据库安全防护体系构建与核心技术解析
引言:从某跨国集团数据泄露事件看Oracle防护困局 2025年1月,某跨国零售企业Oracle数据库遭APT组织"暗夜猎手"攻击,攻击者通过三重渗透路径实现数据窃取: 存储层突破:利用Oracle TDE密钥管理漏洞获取wallet…...
iOS UICollectionViewCell 点击事件自动化埋点
iOS 中经常要进行埋点,我们这里支持 UICollectionViewCell. 进行自动化埋点,思路: 通过hook UICollectionViewCell 的setSelected:方法, 则新的方法中执行埋点逻辑,并调用原来的方法 直接上代码 implementation UICol…...
软件工程---基于构件的软件工程
基于构件的软件工程(CBSE)是一种软件开发方法,通过重用现有的软件构件来构建系统,从而提高开发效率和软件质量。这种方法强调软件系统的模块化设计和构建复用,使得软件开发过程更加高效和灵活。 企业软件开发…...
Redis--单线程模型
目录 一、引言 二、Redis单线程模型 三、原因 四、为什么redis是单线程模型,但他的速度这么快? 五、总结 一、引言 本篇文章就Redis为什么是单线程模型做简单介绍。 二、Redis单线程模型 redis只使用一个线程,处理所有的命令请求&#x…...
NodeJS服务器 + Vue3框架 从搭建服务器 定义接口 到请求数据页面展示
NodeJS服务器 Vue3框架全栈开发 后端项目初始化项目安装express创建服务器server.js启动服务验证服务是否启动成功 前端项目新建vue3项目安装axios启动前端项目启动时报错问题解决 vue页面使用axios调用node接口完整代码页面效果图跨域问题解决 本篇文章主要介绍使用Node.js和…...
3.1、密码学基础
目录 密码学概念与法律密码安全分析密码体制分类 - 私钥密码/对称密码体制密码体制分类 - 公钥密码/非对称密码体制密码体制分类 - 混合密码体制 密码学概念与法律 密码学主要是由密码编码以及密码分析两个部分组成,密码编码就是加密,密码分析就是把我们…...
iOS接入Flutter项目
首先要把iOS项目和flutter项目统一目录下,而且需要注意的是flutter是module。 第一步:Flutter相关内容的创建 module创建命令: flutter create --templatemodule my_flutter,之后再执行 flutter pub get flutter build ios …...
点云配准技术的演进与前沿探索:从传统算法到深度学习融合(4)
4、点云配准面临的挑战与应对策略 4.1 点云配准面临的主要挑战 在点云配准的实际应用中,尽管已经取得了显著的研究成果,但仍然面临着诸多复杂而严峻的挑战,这些挑战严重制约了点云配准技术在更多领域的广泛应用和深入发展。 在自动驾驶场景…...
SEKI —— 基于大型语言模型的自进化与知识启发式神经架构搜索
01、项目概述 我们引入了一种基于新型大型语言模型( LLM )的神经架构搜索( NAS )方法,名为 SEKI 。SEKI 受到现代 LLM 中思维链( CoT )范式的启发,分为两个关键阶段运行:…...
Nat Mach Intell | AI分子对接算法评测
《Nature Machine Intelligence》发表重磅评测,系统评估AI与物理方法在虚拟筛选(VS)中的表现,突破药物发现效率瓶颈。 核心评测体系:三大数据集 研究团队构建了三个新型测试集: TrueDecoy:含14…...
01. HarmonyOS应用开发实践与技术解析
文章目录 前言项目概述HarmonyOS应用架构项目结构Ability生命周期 ArkTS语言特性装饰器状态管理 UI组件与布局基础组件响应式布局样式与主题 页面路由与参数传递页面跳转参数接收 数据绑定与循环渲染数据接口定义循环渲染 条件渲染组件生命周期最佳实践与性能优化组件复用响应式…...
001-码云操作
码云操作 一、配置公钥1.官网地址1.进入 git bash2.查看生成的公钥3.设置到 Gitee4.测试 二、初始化一个项目1.新建仓库 一、配置公钥 方便后续提交代码不用填写密码 1.官网地址 官网地址:https://gitee.com/Git码云教程:https://gitee.com/help/arti…...
10.LED点阵实验
LED点阵是一种由发光二极管排列而成的显示器件,在生活里的各种电器中很常见,像汽车报站器和广告屏等地方都会用到它。 平时用得比较多的是 88 点阵。多个 88 点阵能组合成不同大小的 LED 点阵显示屏,比如 4 个 88 点阵可以拼成一个 1616 点阵…...
cesium+vue3自定义HTML实体弹窗、加高德路网、防实体漂浮、让用户画圆、鹰眼
一、基础使用:Cesium.js基础使用(vue)-CSDN博客 1、基础路径 为 Cesium 库设置一个全局变量 CESIUM_BASE_URL,用于指定 Cesium 的资源文件(如 WebGL shaders、纹理、字体等)的 示例场景:假设你…...
Spring(二)容器-注册
目录 一 定义组件Bean (1)添加组件 (2)获取组件 二 配置类Configuration (1)配置类的作用 三 MVC分层注解 - Controller: - Service: - Repository: 四 批量扫描ComponentScan (1) 默认扫描当前包及其子包 (2) 指定扫描包路径 …...
Java-实现PDF合同模板填写内容并导出PDF文件
可用于公司用户合同导出pdf文件 效果图 一、导入所需要jar包 <!--生成PDF--><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.11</version></dependency><dependency&…...