一、OSSpinLock(已废弃⚠️)
头文件: <libkern/OSAtomic.h>
底层: 原子[自旋锁(CPU 自旋 + LDR / STR 指令)](#自旋锁(CPU 自旋 + LDR / STR 指令) "#102")
objc
#import <libkern/OSAtomic.h>
OSSpinLock lock = OS_SPINLOCK_INIT;
// 加锁
OSSpinLockLock(&lock);
// 解锁
OSSpinLockUnlock(&lock);
底层实现:
objc
Lspinlock:
LDREX R1, [R0] ; 加载锁的值到 R1
CMP R1, #0 ; 如果锁是 0(未加锁)
STREX R2, R3, [R0] ; 尝试设置为 1(加锁)
CMP R2, #0
BNE Lspinlock ; 如果失败,继续自旋
✅ 优点:性能极高(无 syscall 进入内核)。
❌ 缺点:容易造成"优先级反转"(高优先级线程被低优先级线程饿死)。
❌ iOS 10+ 已被 os_unfair_lock 取代。
二、os_unfair_lock
objc
#import <os/lock.h>
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
os_unfair_lock_lock(&lock); // 加锁
os_unfair_lock_unlock(&lock); // 解锁
🔹 底层实现
objc
Llock:
LDREX R1, [R0] ; 加载锁值
CMP R1, #0 ; 检查是否可用
STREX R2, R3, [R0] ; 尝试加锁
CMP R2, #0
BNE Llock ; 如果失败,自旋等待
; 如果锁被占用,则进入内核等待
BL _os_unfair_lock_wait
• os_unfair_lock
先尝试自旋
• 如果竞争激烈,会进入 syscall
休眠
• 避免 CPU 过度消耗,提高系统吞吐量
✅ 优点:比 OSSpinLock 更安全(不会优先级反转)。
✅ 推荐替代 OSSpinLock
,用于多线程同步。
三、pthread_mutex(互斥锁)
objc
#import <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 加锁
pthread_mutex_lock(&mutex);
// 解锁
pthread_mutex_unlock(&mutex);
🔹 底层实现
assembly
Lmutex:
LDREX R1, [R0] ; 读取锁状态
CMP R1, #0
STREX R2, R3, [R0] ; 尝试锁定
BNE Lmutex ; 失败则等待
BL _futex_wait ; 进入系统调用
• 第一次尝试自旋(适用于短时间锁定)
• 如果锁不可用,进入 futex_wait 系统调用
• 性能比 NSLock 高
✅ 优点:比 NSLock
快,适用于 C 代码。
✅ 适用于 POSIX 兼容的锁定需求。
四、NSLock
objc
NSLock *lock = [[NSLock alloc] init];
[lock lock]; // 加锁
[lock unlock]; // 解锁
🔹 底层
• 封装 pthread_mutex 比 pthread_mutex
稍慢
• 适用于 Objective-C 代码
✅ 优点:Objective-C 友好,适用于 Foundation 框架。
五、dispatch_semaphore(信号量)
objc
dispatch_semaphore_t sema = dispatch_semaphore_create(1);
// 线程 1
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
// 线程 2
dispatch_semaphore_signal(sema);
🔹 底层
• 使用 semaphore_wait()
进入 mach_syscall
• 基于 Mach 机制的 semaphore
,比 NSLock 更轻量
• 可用于线程池、资源控制、线程同步、线程池控制
六、NSRecursiveLock
✅ 递归 pthread_mutex,递归锁,比NSLock慢。
允许同一线程多次加锁。
七、dispatch_queue (serial)
✅ 串行队列,用于线程同步。
八、NSCondition
✅ 基于 pthread_cond,条件锁。
九、NSConditionLock
✅ 基于 NSCondition
,比NSCondition
慢。
十、什么是原子(atomic)
原子表示操作不可被中断,即多个线程同时访问变量时,不会被读取到中间状态,保证数据一致性。
@property (atomic, strong) NSString *name;
编译器会自动生成:
objc
- (void)setName:(NSString *)name {
@synchronized(self) { // 保护 name 赋值操作
_name = name; // ARC
}
}
• atomic
保证setName:
线程安全。
• atomic
不能保证多个操作的整体原子性(比如 name.length + name.uppercaseString 仍然可能导致数据竞争)。
✅ 原子操作的底层实现
assembly
Latomic_add:
LDREX R1, [R0] ; 读取 _name
ADD R1, R1, #1 ; R1 = R1 + 1
STREX R2, R1, [R0] ; 尝试写回 _name
CMP R2, #0
BNE Latomic_add ; 如果失败,重试
• LDREX(Load Exclusive)读取变量。
• STREX(Store Exclusive)写入变量,并检查是否有其他线程修改过它。
• 如果 STREX 失败,重新执行(自旋),直到成功。
适用于:
• 多线程数据一致性
• 需要保证单个属性的安全性
• 高并发环境
十一、什么是非原子(nonatomic)
非原子表示操作可能被中断,即多个线程同时访问变量时,可能读取到不完整的数据。
比如:
@property (nonatomic, strong) NSString *name;
编译器不会自动加锁:
objc
- (void)setName:(NSString *)name {
_name = name; // ARC
}
• nonatomic
不保证setName:
线程安全。
• nonatomic
性能更高,因为不需要锁定和等待。
比如:
objc
self.name = @"Hello";
dispatch_async(queue, ^{
self.name = @"World";
});
NSLog(@"%@", self.name); // 可能是 "Hello",可能是 "World",也可能是崩溃
多个线程访问name,可能导致数据竞争。
适用于:
• 单线程环境
• 不涉及数据竞争的情况
• 性能优先的情况
十二、什么是自旋(Spinlock)
自旋(Spinlock) 是 一种高性能的锁 ,线程 不会立即进入休眠 ,而是 持续循环检查锁是否可用(自旋等待)。
objc
#import <libkern/OSAtomic.h>
OSSpinLock lock = OS_SPINLOCK_INIT;
OSSpinLockLock(&lock); // 自旋等待
// 线程安全的操作
OSSpinLockUnlock(&lock); // 释放锁
✅ 自旋锁的核心特点
• 线程不会立即挂起,而是死循环检查锁状态
• 如果锁很快被释放(低竞争),自旋锁更快
• 如果锁被长时间持有(高竞争),自旋锁会浪费 CPU 资源
• 适用于短时间锁定的场景
容易造成优先级反转
,iOS 10 之后,Apple 已废弃 OSSpinLock,使用 os_unfair_lock 代替 OSSpinlock
。
🙋 什么是"优先级反转"?
🔹 假设有两个线程:
• T1(低优先级) 持有 OSSpinLock,但被 T2 抢占 CPU,无法执行
• T2(高优先级) 需要获取 OSSpinLock,但 T1 迟迟不释放
• 结果:T2 只能一直等待,导致"高优先级线程被低优先级线程饿死"
objc
#import <Foundation/Foundation.h>
#import <libkern/OSAtomic.h>
OSSpinLock spinlock = OS_SPINLOCK_INIT;
void lowPriorityTask() {
NSLog(@"低优先级线程尝试加锁...");
OSSpinLockLock(&spinlock); // 加锁
NSLog(@"低优先级线程已获取锁,执行任务...");
sleep(5); // 模拟任务执行时间(期间不释放锁)
NSLog(@"低优先级线程即将释放锁...");
OSSpinLockUnlock(&spinlock); // 解锁
}
void highPriorityTask() {
sleep(1); // 确保低优先级线程先运行
NSLog(@"高优先级线程尝试加锁...");
OSSpinLockLock(&spinlock); // 这里会被阻塞
NSLog(@"高优先级线程已获取锁,执行任务...");
OSSpinLockUnlock(&spinlock);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 低优先级线程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
lowPriorityTask();
});
// 高优先级线程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
highPriorityTask();
});
[[NSRunLoop currentRunLoop] run]; // 让主线程保持运行
}
return 0;
}
🤔 为什么会出现优先级反转?
-
OSSpinLock 是"自旋锁"
T2(高优先级线程)不会进入睡眠,而是不断自旋,占用 CPU 资源,反而导致 T1(低优先级线程)更难执行完成。
-
低优先级线程可能被"抢占"
如果系统有更高优先级的任务,T1(低优先级线程)可能会被"饿死" ,导致它迟迟不能释放锁。
-
高优先级线程无法主动"让步"
T2 不会让出CPU资源,它的调度优先级更高,反而让 T1 更难执行完,形成死循环。
🙋 如何解决"优先级反转"
✅ 使用 os_unfair_lock 代替 OSSpinLock(推荐)
objc
#import <os/lock.h>
os_unfair_lock unfairLock = OS_UNFAIR_LOCK_INIT;
void lowPriorityTask() {
NSLog(@"低优先级线程尝试加锁...");
os_unfair_lock_lock(&unfairLock); // ✅ 替换 `OSSpinLockLock`
NSLog(@"低优先级线程已获取锁,执行任务...");
sleep(5);
NSLog(@"低优先级线程即将释放锁...");
os_unfair_lock_unlock(&unfairLock); // ✅ 替换 `OSSpinLockUnlock`
}
• 如果 T2(高优先级线程)发现锁被占用,它会进入 syscall 休眠,让 T1 继续执行。
• 避免 T2 占用 CPU,导致 T1 不能释放锁,防止死锁。
✅ 使用 [pthread_mutex](#✅ 使用 pthread_mutex 代替 OSSpinLock "#3") 代替 [OSSpinLock](#✅ 使用 pthread_mutex 代替 OSSpinLock "#1")
objc
#import <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void lowPriorityTask() {
NSLog(@"低优先级线程尝试加锁...");
pthread_mutex_lock(&mutex); // ✅ 替换 `OSSpinLockLock`
NSLog(@"低优先级线程已获取锁,执行任务...");
sleep(5);
NSLog(@"低优先级线程即将释放锁...");
pthread_mutex_unlock(&mutex); // ✅ 替换 `OSSpinLockUnlock`
}
• pthread_mutex 是互斥锁,不会自旋,而是让线程进入睡眠等待,提高系统性能
。
• 当高优先级线程等待锁时,低优先级线程会继承高优先级
,加快任务执行,减少等待时间。