锁的作用:保证线程安全。
锁的分类:互斥锁
,自旋锁
,其它比如条件锁,递归锁,信号量都是上层的封装和实现。
互斥锁
防止两条线程同时同一公共资源进行读写的机制。当获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒。
互斥锁分为:
- 递归锁:可重入锁,同一个线程在锁释放前可再次获取锁,可以递归调用
- 非递归锁:不可重入,必须等锁释放后才能再次获取锁。
自旋锁
线程反复检查锁变量是否可用。由于线程在这一过程中保持执行,因此是一种忙等待。一旦获取自旋锁,线程会一直保持该锁,直到显式释放。
优点:避免了线程的上下文调度开销,因此对于线程只会阻塞很短时间的场景很有用。
互斥锁和自旋锁区别
- 互斥锁在线程没获取到锁时,线程会进入休眠状态,等锁被释放时会被唤醒
- 自旋锁的线程会一直处于等待(忙等待),不会进入休眠,效率高。
自旋锁种类
1. OSSpinLock
自旋锁存在不安全机制,因为自旋锁会让线程处于忙等待,因此高优先级的线程可能一直占用CPU,造成低优先级的任务无法执行,不能释放锁。
2. 读写锁
- 写是排他的,同时只能有一个写或者多个读,但不能同时读写。
- 若当前没有读写,写可以立即获得读写锁,否则必须等待没有任何读写者。如果没有写,读可以立即获得锁,否则不谢等写释放锁。
互斥锁
pthread_mutex
全局声明
#import <pthread.h>
pthread_mutex_t _lock;
- (void)addCount {
pthread_mutex_lock(&_lock);
for (int i = 0; i < 10; i++) {
self.count += 1;
NSLog(@"----- %d", self.count);
}
pthread_mutex_unlock(&_lock);
}
- (void)method1 {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self addCount];
});
}
- (void)method2 {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self addCount];
});
}
@synchronized
@synchronized (self) {
for (int i = 0; i < 10; i++) {
self.count += 1;
NSLog(@"----- %d", self.count);
}
}
- 不能使用非OC对象作为加锁条件——id2data中接收参数为id类型
- 多次锁同一个对象会有什么后果吗——会从高速缓存中拿到data,所以只会锁一次对象
- 都说@synchronized性能低——是因为在底层增删改查消耗了大量性能
- 加锁对象不能为nil,否则加锁无效,不能保证线程安全
NSLock
是对互斥锁的简单封装
[self.lock lock];
//[self.lock lock] 重复两次会造成线程永久锁死
for (int i = 0; i < 10; i++) {
self.count += 1;
NSLog(@"----- %d", self.count);
}
[self.lock unlock];
NSLock在解锁前不能重复加锁。
NSRecursiveLock
可重复加锁
dispatch_semaphore
NSCondition
NSConditionLock
性能对比
性能对比.png
参考:https://juejin.im/post/5ec9f3ec6fb9a047f0125fd2#heading-32
网友评论