【iOS】锁[特殊字符]

文章目录

  • 前言
  • 1️⃣什么是锁🔒?
    • 1.1 基本概念
    • 1.2 锁的分类
  • 2️⃣OC 中的常用锁
    • 2.1 OSSpinLock(已弃用):“自旋锁”的经典代表
      • 为什么尽量在开发中不使用自旋锁
        • 自旋锁的本质缺陷:忙等待(Busy Waiting)
        • os_unfair_lock的局限性:不适用于复杂场景
        • 苹果的官方建议:优先使用更高效的锁
    • 2.2 dispatch_semaphore_t(GCD 信号量):“高性能通用锁”
    • 2.3 pthread_mutex(POSIX 互斥锁):“最通用的系统级锁”
    • 2.4 NSLock:“OC 层面的互斥锁封装”
    • 2.5 @synchronized:“OC 特有的语法糖”
    • 2.6 NSCondition:“条件等待锁”(生产者-消费者型)
  • 3️⃣锁的对比与选择
  • 4️⃣常见陷阱
    • 4.1 死锁(Deadlock)
    • 4.2 锁内执行耗时操作
    • 4.3 错误使用锁对象
  • 总结

前言

  在前段时间学习dyld时,接触到了锁,由于并不是很清楚锁的知识,导致涉及到锁的其他内容也有点懵懂,再加上,想要深入了解OC中的多线程,锁是前提预备知识,所以笔者对锁进行简单学习并撰写了这篇博客。

1️⃣什么是锁🔒?

1.1 基本概念

百度解释如下:

锁是编程中用于协调多个线程或进程对共享资源访问的机制,主要用于防止并发冲突、确保数据一致性和程序正确性。 ‌

互斥性‌:保证同一时刻只有一个线程/进程访问共享资源,避免数据竞争。

协调线程/进程的执行顺序,确保操作原子性(全部完成或全部不完成)。

简单来说,锁就像一个“开关”,在同一时间只允许一个线程“进入”某个代码段或访问某个资源,其他线程需要等待,直到锁被释放。

1.2 锁的分类

根据分类标准,一般情况下我们把锁分为一下7大类别:

(1)悲观锁和乐观锁

(2)公平锁和非公平锁

(3)共享锁和独占锁

(4)可重入锁和非可重入锁

(5)自旋锁和非自旋锁

(6)偏向锁、轻量级锁和重量级锁

(7)可中断锁和不可中断锁

在OC中,锁的大类就两种自旋锁和互斥锁,可以细分为以下几类:

类型特点典型代表
互斥锁(Mutex)同一时间仅允许一个线程加锁,不可重入pthread_mutex、NSLock
自旋锁线程会反复检查变量是否可用OSSpinLock(已弃用)、atomic
条件锁(Condition)等待特定条件满足后再加锁NSCondition、pthread_cond_t
递归锁(Recursive Lock)允许同一线程多次加锁(需对应次数解锁)pthread_mutex(recursive)、NSRecursiveLock
信号量(Semaphore)控制并发线程数量(非严格互斥)dispatch_semaphore_t
语言特性锁OC 语法糖,简化锁操作@synchronized(互斥锁)

另外还有一个读写锁:读写锁实际是一种特殊的自旋锁。将对共享资源的访问分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。这种锁相对于自旋锁而言,能提高并发性。
在这里插入图片描述

2️⃣OC 中的常用锁

2.1 OSSpinLock(已弃用):“自旋锁”的经典代表

原理:自旋锁(Spin Lock)通过忙等待(循环检查锁状态)实现互斥。当锁被占用时,其他线程不会休眠,而是不断循环尝试获取锁,直到成功。

历史:OSSpinLock 曾是 OC 中最快的锁(无系统调用,纯用户态操作),但因 优先级反转问题(低优先级线程持有锁时,高优先级线程会疯狂自旋抢占 CPU,导致系统卡顿)在 iOS 10 后被弃用。

示例代码(仅作原理参考,禁止在生产环境使用):

#import <os/lock.h>os_unfair_lock lock = OS_UNFAIR_LOCK_INIT; // OSSpinLock 已弃用,替代方案是 os_unfair_lock(本质是改良的自旋锁)- (void)threadSafeMethod {os_unfair_lock_lock(&lock);// 临界区:访问共享资源os_unfair_lock_unlock(&lock);
}

注意:os_unfair_lock是 OSSpinLock 的替代方案,但仍为自旋锁,适用于轻量级同步(如单次数据访问),避免在锁内执行耗时操作。

为什么尽量在开发中不使用自旋锁

自旋锁的本质缺陷:忙等待(Busy Waiting)

os_unfair_lock的底层实现与被弃用的 OSSpinLock类似,均基于自旋锁机制。当锁被其他线程占用时,当前线程会进入一个 无限循环(忙等待),不断检查锁是否释放。这种机制会导致以下问题:

1.cpu资源的浪费

自旋锁的“忙等待”会持续占用 CPU 时间片,即使线程并未执行任何有效操作。在高负载场景(如多线程竞争激烈)下,大量线程空转会导致 CPU 使用率飙升,甚至引发系统卡顿。

示例对比:

  • 传统互斥锁(如 pthread_mutex):锁被占用时,线程会主动休眠(释放 CPU),等待锁释放后被唤醒。
  • 自旋锁(如 os_unfair_lock):锁被占用时,线程持续空转,CPU 资源被无意义消耗。

2.优先级反转

当低优先级的线程持有锁时,高优先级线程会因不断尝试获取锁而频繁抢占cpu,导致低优先级线程无法运行(被“饿死”)。这种现象被称为优先级反转,会严重破坏系统的实时性和公平性。

eg:低优先级线程 A 持有 os_unfair_lock。高优先级线程 B 尝试获取锁,进入忙等待,持续占用 CPU。系统被迫频繁调度高优先级线程 B,低优先级线程 A 无法获得执行机会。

os_unfair_lock的局限性:不适用于复杂场景

尽管 os_unfair_lock比 OSSpinLock更轻量(减少了部分内存屏障),但它的设计目标仅限于 轻量级、短时间的临界区保护(如单次内存访问)。对于以下场景,它无法提供可靠支持:

1. 长时间持有锁的操作

如果临界区内需要执行耗时操作(如文件 I/O、网络请求、复杂计算),os_unfair_lock的忙等待会导致线程长时间占用 CPU,严重影响其他任务的执行效率。

eg:

// 错误用法:临界区执行耗时操作(如读取大文件)
os_unfair_lock_lock(&lock);
NSData *largeData = [NSData dataWithContentsOfFile:@"/bigfile.dat"]; // 耗时操作
os_unfair_lock_unlock(&lock);

此时,线程会因忙等待导致 CPU 空转,而文件读取的 I/O 操作本身是阻塞的(无需 CPU 参与),造成资源浪费。

2.递归锁需求

os_unfair_lock不支持递归加锁(同一线程无法多次获取同一把锁)。如果代码中存在递归调用(如方法 A 调用方法 B,两者都需要加同一把锁),会导致死锁。

eg:

- (void)recursiveMethod {os_unfair_lock_lock(&lock);// 递归调用自身[self recursiveMethod]; // 第二次加锁会失败,导致死锁os_unfair_lock_unlock(&lock);
}
苹果的官方建议:优先使用更高效的锁

苹果在官方文档中明确推荐,避免使用自旋锁(包括 os_unfair_lock)处理需要长时间持有或高竞争的场景,并提供了更优的替代方案:

1. dispatch_semaphore_t(GCD 信号量)

基于内核信号量实现,锁被占用时线程会休眠(释放 CPU),支持并发控制(计数 >1 时允许多线程同时访问)。适用于轻量级同步、限制并发任务数(如限制同时下载的文件数)等场景。

2. pthread_mutex(POSIX 互斥锁)

支持递归锁(通过 PTHREAD_MUTEX_RECURSIVE类型),内核级实现,稳定性高。适用于需要递归加锁或多线程频繁竞争的场景(如嵌套方法调用)。

3. NSLock(OC 层面封装)

API 简洁,基于 pthread_mutex实现,支持 tryLock非阻塞加锁。适用于简单的线程同步(如保护单次数据访问)。

2.2 dispatch_semaphore_t(GCD 信号量):“高性能通用锁”

信号量(Semaphore)是基于计数器的一种多线程同步机制,用来管理对资源的并发访问。信号量就是一种可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问之前,加上信号量的处理,则可告知系统按照我们指定的信号量数量来执行多个线程。

相关函数:

  • dispatch_semaphore_t、dispatch_semaphore_create(long value):创建信号量,初始化计数(通常为 1 时表示互斥锁)。参数为信号量的初值,小于零就会返回NULL。

  • 线程加锁时调用 dispatch_semaphore_wait减少计数(若计数为 0 则阻塞)。long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); :等待降低信号量,接收一个信号和时间值(多为DISPATCH_TIME_FOREVER)。若信号的信号量为0,则会阻塞当前线程,直到信号量大于0或者经过输入的时间值。若信号量大于0,则会使信号量减1并返回,程序继续住下执行。

  • 解锁时调用 dispatch_semaphore_signal增加计数(唤醒等待线程)。long dispatch_semaphore_signal(dispatch_semaphore_t dsema); :提高信号量, 使信号量加1并返回 在dispatch_semaphore_wait和dispatch_semaphore_signal这两个函数中间的执行代码,每次只会允许限定数量的线程进入,这样就有效的保证了在多线程环境下,只能有限定数量的线程进入。

特点

  • 性能接近 OSSpinLock(底层通过内核信号量实现,但比传统锁更高效)。
  • 支持控制并发线程数量(计数 >1 时为“并发锁”,允许指定数量线程同时访问)。

示例代码

/*
// 初始化信号量(计数为 1,即互斥锁)
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);- (void)threadSafeMethod {// 加锁:计数减 1(若计数为 0 则阻塞当前线程)dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);// 临界区:访问共享资源// 解锁:计数加 1(唤醒等待线程)dispatch_semaphore_signal(semaphore);
}
*///具体实例
#import <Foundation/Foundation.h>
#import <os/lock.h>int main(int argc, const char * argv[]) {@autoreleasepool {// 共享资源:一个需要线程安全的计数器__block int counter = 0;// 初始化信号量(计数为 1,即互斥锁;若计数>1,允许指定数量线程同时访问)dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);// 模拟 10 个线程同时修改计数器for (int i = 0; i < 10; i++) {dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{for (int j = 0; j < 1000; j++) {// 加锁:计数减 1(若计数为 0 则阻塞)dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);// 临界区:修改共享资源(需保证原子性)counter++;// 解锁:计数加 1(唤醒等待线程)dispatch_semaphore_signal(semaphore);}});}// 等待所有线程完成后打印结果(实际开发中需用更严谨的同步机制)sleep(2);NSLog(@"最终计数器值:%d", counter); // 应输出 10000(10 线程×1000 次)}return 0;
}

运行测试:

请添加图片描述

适用场景

  • 需要高性能互斥的场景(如高频数据读写)。
  • 控制并发任务数量(如限制同时下载的任务数,计数设为 3)。

2.3 pthread_mutex(POSIX 互斥锁):“最通用的系统级锁”

POSIX 标准的互斥锁(Mutex),通过内核实现线程阻塞。支持两种类型:

  • 普通锁(PTHREAD_MUTEX_INITIALIZER):不可重入,同一线程重复加锁会死锁。
  • 递归锁(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP):允许同一线程多次加锁(需对应次数解锁)。

示例代码

/*
#import <pthread.h>// 初始化递归锁(允许同一线程多次加锁)
pthread_mutex_t recursiveLock;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); // 设置为递归锁
pthread_mutex_init(&recursiveLock, &attr);- (void)recursiveMethod {pthread_mutex_lock(&recursiveLock);// 临界区(可能递归调用自身)pthread_mutex_unlock(&recursiveLock);
}// 销毁锁(避免内存泄漏)
pthread_mutex_destroy(&recursiveLock);
pthread_mutexattr_destroy(&attr);
*///具体实例
#import <Foundation/Foundation.h>
#import <os/lock.h>
#import <pthread/pthread.h>int main(int argc, const char * argv[]) {@autoreleasepool {// 共享资源:一个需要线程安全的数组__block NSMutableArray *sharedArray = [NSMutableArray array];// 初始化递归锁(允许同一线程多次加锁)pthread_mutex_t recursiveLock;pthread_mutexattr_t attr;pthread_mutexattr_init(&attr);pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); // 关键:设置为递归锁pthread_mutex_init(&recursiveLock, &attr);// 线程 1:嵌套调用方法dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{pthread_mutex_lock(&recursiveLock); // 第一次加锁[sharedArray addObject:@"A"];// 嵌套调用(需再次加锁)pthread_mutex_lock(&recursiveLock); // 第二次加锁(递归锁允许)[sharedArray addObject:@"B"];pthread_mutex_unlock(&recursiveLock); // 第一次解锁pthread_mutex_unlock(&recursiveLock); // 第二次解锁});// 线程 2:直接访问共享资源dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{pthread_mutex_lock(&recursiveLock);[sharedArray addObject:@"C"];pthread_mutex_unlock(&recursiveLock);});// 等待线程完成后打印结果sleep(2);NSLog(@"共享数组:%@", sharedArray); // 应输出 ["A", "B", "C"](顺序可能不同)}return 0;
}

运行测试:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

特点

  • 性能稳定,兼容所有 iOS 版本。
  • 递归锁适合嵌套调用场景(如方法 A 调用方法 B,两者都需要加同一把锁)。

2.4 NSLock:“OC 层面的互斥锁封装”

原理:NSLock是 pthread_mutex的 OC 封装,提供了更简洁的 API(如 lock、unlock、tryLock)。默认是普通锁(不可重入),但可通过 NSRecursiveLock子类实现递归锁。

示例代码

/*
// 普通互斥锁
NSLock *lock = [[NSLock alloc] init];- (void)threadSafeMethod {[lock lock];// 临界区[lock unlock];
}// 尝试加锁(非阻塞,返回 BOOL 表示是否成功)
if ([lock tryLock]) {// 临界区[lock unlock];
}// 递归锁(允许同一线程多次加锁)
NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];
*///具体实例
#import <Foundation/Foundation.h>
#import <os/lock.h>
#import <pthread/pthread.h>int main(int argc, const char * argv[]) {@autoreleasepool {// 共享资源:一个需要线程安全的用户信息字典__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];// 初始化普通互斥锁NSLock *lock = [[NSLock alloc] init];// 创建一个调度组dispatch_group_t group = dispatch_group_create();// 线程 1:修改用户信息dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{[lock lock]; // 加锁userInfo[@"name"] = @"张三";userInfo[@"age"] = @25;[lock unlock]; // 解锁});// 线程 2:读取用户信息(需等待线程 1 解锁)dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{[lock lock]; // 加锁(若线程 1 未解锁会阻塞)NSString *name = userInfo[@"name"];NSNumber *age = userInfo[@"age"];NSLog(@"用户信息:%@,%@", name, age); // 应输出 "张三",25[lock unlock]; // 解锁});// 等待所有异步任务完成dispatch_group_wait(group, DISPATCH_TIME_FOREVER);}return 0;
}

运行测试:

请添加图片描述

注意:NSLock的 unlock必须与 lock成对出现,否则可能导致死锁(如异常未捕获导致 unlock未执行)。

2.5 @synchronized:“OC 特有的语法糖”

原理:@synchronized是 OC 编译器提供的语法糖,底层通过哈希表将锁对象映射到内核互斥锁。语法简洁,无需手动管理锁的创建和销毁。

示例代码

// 以某个对象(如 self)为锁的标识
- (void)threadSafeMethod {@synchronized (self) { // 锁对象为 self// 临界区:访问共享资源}
}// 也可以用其他对象作为锁(推荐专用锁对象,避免与其他代码冲突)
NSObject *lockObj = [[NSObject alloc] init];
@synchronized (lockObj) {// 临界区
}

特点

  • 代码简洁,无需手动加锁/解锁(自动管理)。
  • 锁对象需唯一(不同锁对象无法同步)。
  • 性能较差(底层涉及哈希表查找和内核调用),适合小范围同步(如单次数据访问)。

2.6 NSCondition:“条件等待锁”(生产者-消费者型)

原理NSConditionpthread_cond_t的 OC 封装,结合了互斥锁和条件变量。允许线程在条件不满足时等待(释放锁并休眠),条件满足时被唤醒(重新加锁)。

示例代码(生产者-消费者模型):

@interface ProducerConsumer : NSObject {NSMutableArray *_queue;NSCondition *_condition;NSInteger _maxCount;
}
@end@implementation ProducerConsumer
- (instancetype)init {if (self = [super init]) {_queue = [NSMutableArray array];_condition = [[NSCondition alloc] init];_maxCount = 10; // 队列最大容量}return self;
}// 生产者:添加数据(队列满时等待)
- (void)produce {[_condition lock];while (_queue.count >= _maxCount) {[_condition wait]; // 条件不满足,释放锁并休眠}[_queue addObject:@(arc4random_uniform(100))];[_condition signal]; // 唤醒一个等待的消费者[_condition unlock];
}// 消费者:取出数据(队列空时等待)
- (void)consume {[_condition lock];while (_queue.count == 0) {[_condition wait]; // 条件不满足,释放锁并休眠}id obj = _queue.firstObject;[_queue removeObjectAtIndex:0];[_condition signal]; // 唤醒一个等待的生产者[_condition unlock];
}
@end

适用场景:需要线程间协调(如“生产者-消费者”模型),等待特定条件满足后再执行。

3️⃣锁的对比与选择

OC 中常见锁的性能(从高到低,单线程加锁/解锁耗时):

os_unfair_lock ≈ dispatch_semaphore_t > pthread_mutex(递归) > NSRecursiveLock > NSLock > @synchronized

锁的选择:

  1. 性能要求高:优先选 dispatch_semaphore_t(通用)或 os_unfair_lock(轻量同步)。
  2. 需要递归:选 pthread_mutex(recursive)NSRecursiveLock
  3. 代码简洁性:选 @synchronized(适合小范围同步)。
  4. 条件等待:选 NSCondition(如生产者-消费者模型)。

4️⃣常见陷阱

4.1 死锁(Deadlock)

原因:多个线程互相等待对方释放锁(如线程 A 持有锁 1 等待锁 2,线程 B 持有锁 2 等待锁 1)。

解决

  • 避免嵌套加锁(如非必要不使用递归锁)。
  • 统一加锁顺序(所有线程按相同顺序获取锁)。

4.2 锁内执行耗时操作

原因:锁的加锁/解锁涉及内核调用,若临界区内执行耗时操作(如 IO、大量计算),会导致其他线程长时间等待,降低并发效率。

解决

  • 将耗时操作移到锁外(如先读取数据到临时变量,再在锁内处理)。

4.3 错误使用锁对象

原因@synchronized使用动态对象(如可能被释放的 self)作为锁标识,或不同线程使用不同的锁对象。

解决

  • @synchronized的锁对象需是生命周期稳定的(如专用 NSObject实例)。

总结

OC 中的锁机制需根据场景选择:

  • 轻量同步dispatch_semaphore_tos_unfair_lock(性能最优)。
  • 递归需求pthread_mutex(recursive)NSRecursiveLock
  • 代码简洁@synchronized(小范围使用)。
  • 条件协调NSCondition(如生产者-消费者)。

核心原则:锁的范围尽可能小(仅保护临界区),避免死锁,优先使用系统提供的高性能锁(如 GCD 信号量)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/92525.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/92525.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在easyui中如何设置自带的弹窗,有输入框

这个就是带input的确认弹框&#xff08;$.messager.prompt&#xff09;// 使用prompt并添加placeholder提示 $.messager.prompt(确认, 确定要将事故记录标记为 statusText 吗&#xff1f;, function(r) {if (r) {// r 包含用户输入的内容var remark r.trim();// 验证输入不为…

Android-API调用学习总结

一、Postman检查API接口是否支持1.“HTTP Request” 来创建一个新的请求。——请求构建界面&#xff0c;这是你进行所有 API 调用的地方。2.设置请求方法和 URL&#xff1a;选择请求方法&#xff1a; 在 URL 输入框左侧&#xff0c;有一个下拉菜单。点击它&#xff0c;选择你想…

《计算机网络》实验报告一 常用网络命令

目 录 1、实验目的 2、实验环境 3、实验内容 3.1 ping基本用法 3.2 ifconfig/ipconfig基本用法 3.3 traceroute/tracert基本用法 3.4 arp基本用法 3.5 netstat基本用法 4、实验结果与分析 4.1 ping命令的基本用法 4.2 ifconfig/ipconfig命令的基本用法 4.3 tracer…

MySQL深度理解-深入理解MySQL索引底层数据结构与算法

1.引言在项目中会遇到各种各样的慢查询的问题&#xff0c;对于千万级的表&#xff0c;如果使用比较笨的查询方式&#xff0c;查询一条SQL可能需要几秒甚至几十秒&#xff0c;如果将索引设置的比较合理&#xff0c;可以将查询变得仍然非常快。2.索引的本质索引&#xff1a;帮助M…

Django母婴商城项目实践(九)- 商品列表页模块

9、商品列表页模块 1、业务逻辑 商品模块分为:商品列表页 和 商品详情页 商品列表页将所有商品按照一定的规则排序展示,用于可以从销量、价格、上架时间和收藏数量设置商品的排序方式,并且在商品左侧设置分类列表,选择某一个分类可以筛选出对应的商品信息。 商品列表页…

8、STM32每个系列的区别

1、F1和F4的系列的区别 F1采用Crotex M3内核&#xff0c;F4采用Crotex M4内核。F4比F1的主频高。F4具有浮点数运算单元&#xff0c;F1没有浮点单元。F4的具备增强的DSP指令集。F407的执行16位DSP指令的时间只有F1的30%~70%。F4执行32位DSP指令的时间只有F1的25% ~ 60%。F1内部S…

DeepSPV:一种从2D超声图像中估算3D脾脏体积的深度学习流程|文献速递-医学影像算法文献分享

Title题目DeepSPV: A deep learning pipeline for 3D spleen volume estimation from 2Dultrasound imagesDeepSPV&#xff1a;一种从2D超声图像中估算3D脾脏体积的深度学习流程01文献速递介绍1.1 临床背景 脾肿大指脾脏增大&#xff0c;是多种潜在疾病的重要临床指标&#x…

病历数智化3分钟:AI重构医院数据价值链

一、方案概述本方案针对某省医联体医院病例数据管理需求&#xff0c;通过AI技术实现病历数字化→信息结构化→数据应用化的全流程改造。系统采用双端协同架构&#xff1a; - 普通用户端&#xff1a;为一线医护人员提供病历拍摄、AI识别修正、安全上传功能 - 管理员后台&#…

CSS+JavaScript 禁用浏览器复制功能的几种方法

&#x1f6e1;️ 禁用浏览器复制功能完整指南 网页中禁用用户的复制功能&#xff0c;包括 CSS 方法、JavaScript 方法、综合解决方案以及实际应用场景。适用于需要保护内容版权、防止恶意爬取或提升用户体验的场景。 &#x1f4cb; 目录 &#x1f680; 快速开始&#x1f3a8…

Java 虚拟线程在高并发微服务中的实战经验分享

Java 虚拟线程在高并发微服务中的实战经验分享 虚拟线程&#xff08;Virtual Threads&#xff09;作为Java 19引入的预览特性&#xff0c;为我们在高并发微服务场景下提供了一种更轻量、易用的并发模型。本文结合真实生产环境&#xff0c;讲述在Spring Boot微服务中引入和使用虚…

《拆解WebRTC:NAT穿透的探测逻辑与中继方案》

WebRTC以其无需插件的便捷性&#xff0c;成为连接全球用户的隐形桥梁。但很少有人知晓&#xff0c;每一次流畅的视频对话背后&#xff0c;都藏着一场与网络边界的无声博弈——NAT&#xff0c;这个为缓解IPv4地址枯竭而生的技术&#xff0c;既是网络安全的屏障&#xff0c;也是端…

前端开发 React 组件优化

1. 使用 React.memo 进行组件优化问题&#xff1a;当父组件重新渲染时&#xff0c;子组件也会重新渲染&#xff0c;即使它的 props 没有变化。解决方案&#xff1a;使用 React.memo 包裹子组件&#xff0c;让其只在 props 变化时才重新渲染。示例场景&#xff1a;展示一个显示计…

变频器实习DAY12

目录变频器实习DAY12一、继续&#xff0c;柔性平台测试&#xff01;上午 王工Modbus新功能测试下午 柔性平台继续按照说明书再测一遍附加的小知识点中国狸花猫.git文件附学习参考网址欢迎大家有问题评论交流 (* ^ ω ^)变频器实习DAY12 一、继续&#xff0c;柔性平台测试&…

Redis--多路复用

&#x1f9e9; 一、什么是“客户端连接”&#xff1f;所谓 客户端连接 Redis&#xff0c;指的是&#xff1a;一个程序&#xff08;客户端&#xff09;通过网络连接到 Redis 服务端&#xff08;比如 127.0.0.1:6379&#xff09;&#xff0c;建立一个 TCP 连接&#xff0c;双方可…

数组——初识数据结构

一维数组数组的创建数组是一种相同类型元素的集合数组的创建方式C99 中引入了变长数组的概念&#xff0c;变长数组支持数组的大小使用变量来指定明显这里的vs2019不支持变长数组数组初始化和不完全初始化第二个数组就是典型的不完全初始化&#xff0c;开辟了10个空间&#xff0…

技术速递|使用 Semantic Kernel 与 A2A 协议构建多智能体解决方案

作者&#xff1a;卢建晖 - 微软高级云技术布道师 翻译/排版&#xff1a;Alan Wang 在快速发展的 AI 应用开发领域&#xff0c;能够协调多个智能体已成为构建复杂企业级解决方案的关键。虽然单个 AI 智能体擅长特定任务&#xff0c;但复杂的业务场景往往需要跨平台、跨框架甚至跨…

前端跨域请求原理及实践

在前端开发中&#xff0c;"跨域"是一个绕不开的话题。当我们的页面尝试从一个域名请求另一个域名的资源时&#xff0c;浏览器往往会抛出类似Access to fetch at xxx from origin xxx has been blocked by CORS policy的错误。下面将深入探讨跨域请求的底层原理&#…

SpringBoot07-数据层的解决方案:SQL

一、内置数据源 1-1、【回顾】Druid数据源的配置 druid的两种导入格式 1-2、springboot提供的3种内置数据源的配置 若是不配置Druid&#xff0c; springboot提供了3中默认的数据源配置&#xff0c;它们分别是&#xff1a; 1. HikariCP&#xff08;默认&#xff09; 从 Spring…

前端自动化埋点:页面模块级行为跟踪与问题定位系统​​的技术设计方案

一、核心设计目标​​精细化监控​​&#xff1a;定位到页面中​​单个模块​​的曝光、点击等行为。​​低侵入性​​&#xff1a;业务代码与埋点逻辑解耦&#xff0c;降低开发维护成本。​​链路可追踪​​&#xff1a;串联用户从曝光到操作的完整行为路径。​​实时性​​&a…

Node.js 与 Java 性能对比

一、核心架构与任务模型对比Node.js 单线程事件循环 非阻塞I/O 通过V8引擎执行JavaScript&#xff0c;采用事件驱动模型&#xff0c;所有I/O操作&#xff08;如网络请求、文件读写&#xff09;均为非阻塞。单线程处理所有请求&#xff0c;但通过事件循环&#xff08;Event Loo…