1.###锁的类型
1.互斥锁:保证同时只有一个线程能够访问。当获取锁操作失败时,线程进入休眠,等待锁被释放时被唤醒。
2.自旋锁:保证同时只有一个线程能够访问(这点和互斥锁相同);只是自旋锁不会引起调用者的休眠。
如果自旋锁已经被别的执行单元持有,调用者就一直循环尝试,直到该自旋锁被持有者释放。
因为没有休眠,所以效率高于互斥锁;但是,性能方面一般。
3.递归锁: 加了递归功能的互斥锁。
2.###优缺点
1.自旋锁的循环访问,会占用CPU资源,降低CPU的效率;
2.在使用自旋锁时有可能造成死锁,当地柜调用时有可能造成死锁;
3.加锁原理
自旋锁:线程一直runing(加锁->解锁),检查锁状态,机制较简单。
互斥锁:线程sleep(加锁)-> running (解锁),过程中有上下文的切换,CPU的抢占,信号的发送等开销。
4.###常用的锁
1.@synchronized
@synchronized(self) {
}
结论:1.是对互斥锁的一种封装;2.具体点,是种递归锁,内部搭配nil防止死锁;3.通过表的结构存要锁的对象;4.表内部的对象又是通过哈希存储的。
坑点:在大量线程异步同时操作同一个对象时,因为递归锁会不停的alloc/release,在某一个对象会是nil;而此时@synchronized(obj) 会判断obj == nil,就不会再加锁,导致线程访问冲突。
2.NSLock
NSlock 可以解决大量异步线程同时操作同一个对象的内存安全问题;
NSLock 是对pthread_mutex(互斥锁)的封装,有超时控制。
坑点:当NSLock对同一个线程加锁两次,就会造成死锁;即不能实现递归锁。
//NSLock
- (void)NSLock_crash {
NSLock *lock = [[NSLock alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^testBlock)(int);
testBlock = ^(int value) {
[lock lock];
if (value > 0) {
NSLog(@"value-->%d",value);
testBlock(value-1);//递归调用,用递归锁
}
[lock unlock];
};
testBlock(10);
});
}
//递归锁NSRecursiveLock
- (void)NSRecursiveLock_NO_crash {
NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^testBlock)(int);
testBlock = ^(int value) {
[lock lock];
if (value > 0) {
NSLog(@"value-->%d",value);
testBlock(value-1);//递归调用,用递归锁 , 同一线程多次加锁
}
[lock unlock];
};
testBlock(10);
});
}
3. NSRecursiveLock
1.是对pthread_mutex(互斥锁)的封装,不同的是加Recursive递归调用功能;
2.同样也有timeout超时控制;
4. NSCondition 使用的相对较少
1.也是对互斥锁的封装;2.使用了wait信号量可以让当前线程处于等到中;3.使用signal信号可以告诉其他某一个线程不用再等待了,可以继续执行;4.内部还有一个broadcast(广播)信号,用于发送(signal)信号给其他所有线程用法;
5.NSConditionLock类似于信号量
1.NSConditionLock 是 对NSCondition+线程数的封装,即NSConditionLock = NSCondition + lock
internal var _thread: _swift_CFThreadRef?:_thread就是当前可以同事操作的线程数,通过搭配NSCondition可以达到dispatch_semaphore的效果
lock(before: Date.distantFuture):也有超时时间
- (void)testConditonLock{
// 信号量
NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:2];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[conditionLock lockWhenCondition:1];
NSLog(@"线程 1");
[conditionLock unlockWithCondition:0];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[conditionLock lockWhenCondition:2];
NSLog(@"线程 2");
[conditionLock unlockWithCondition:1];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[conditionLock lock];
NSLog(@"线程 3");
[conditionLock unlock];
});
}
6.dispatch_semaphore
1.dispathch_semaphore 是GCD用来同步的一种方式,与他相关的只有三个函数,一个是创建信号量,一个是等待信号量,一个是发送信号量。
dispathch_semaphore NSConditionLock NSCOnditin ,都是基于信号量同步方式,但是NSCondition信号智能发送,不能保存(如果没有线程在等待,则发送信号量会失败);而dispathch_semaphore 能保存发送的信号。
2.dispatch_semaphore 的核心是 dispatch_semaphore_t 类型的信号量。
dispatch_semaphore_create(1) 方法可以创建一个 dispatch_semaphore_t 类型的信号量,设定信号量的初始值为 1。注意,这里的传入的参数必须大于或等于 0,否则 dispatch_semaphore_create 会返回 NULL。
dispatch_semaphore_wait(signal, overTime); 方法会判断 signal 的信号值是否大于 0。大于 0 不会阻塞线程,消耗掉一个信号,执行后续任务。如果信号值为 0,该线程会和 NSCondition 一样直接进入 waiting 状态,等待其他线程发送信号唤醒线程去执行后续任务,或者当 overTime 时限到了,也会执行后续任务。
dispatch_semaphore_signal(signal); 发送信号,如果没有等待的线程接受信号,则使 signal 信号值加一(做到对信号的保存)。
3.一个 dispatch_semaphore_wait(signal, overTime); 方法会去对应一个 dispatch_semaphore_signal(signal); 看起来像 NSLock 的 lock 和 unlock,其实可以这样理解,区别只在于有信号量这个参数,lock unlock 只能同一时间,一个线程访问被保护的临界区,而如果 dispatch_semaphore 的信号量初始值为 x ,则可以有 x 个线程同时访问被保护的临界区。
7.OSSpinLock - os_unfair_lock
在iOS10 之前,OSSpinLock 是一种自旋锁,也只有加锁,解锁,尝试加锁三个方法。和 NSLock 不同的是 NSLock 请求加锁失败的话,会先轮询,但一秒过后便会使线程进入 waiting 状态,等待唤醒。而 OSSpinLock 会一直轮询,等待时会消耗大量 CPU 资源,不适用于较长时间的任务。而因为OSSpinLock不再线程安全,在iOS10之后OSSpinLock被废弃内部封装了os_unfair_lock
,os_unfair_lock
也是一种互斥锁不会忙等。 `typedef int32_t OSSpinLock OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock);
8.读写锁
读写锁是一种特殊的自旋锁,他能做到多读单写;实现: 并发队列+ dispatch_barrier_async
########### .h文件
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface RF_RWLock : NSObject
// 读数据
- (id)rf_objectForKey:(NSString *)key;
// 写数据
- (void)rf_setObject:(id)obj forKey:(NSString *)key;
@end
NS_ASSUME_NONNULL_END
########### .m文件
#import "RF_RWLock.h"
@interface RF_RWLock ()
// 定义一个并发队列:
@property (nonatomic, strong) dispatch_queue_t concurrent_queue;
// 用户数据中心, 可能多个线程需要数据访问:
@property (nonatomic, strong) NSMutableDictionary *dataCenterDic;
@end
@implementation RF_RWLock
- (id)init{
self = [super init];
if (self){
// 创建一个并发队列:
self.concurrent_queue = dispatch_queue_create("read_write_queue", DISPATCH_QUEUE_CONCURRENT);
// 创建数据字典:
self.dataCenterDic = [NSMutableDictionary dictionary];
}
return self;
}
#pragma mark - 读数据
- (id)rf_objectForKey:(NSString *)key{
__block id obj;
// 同步读取指定数据:
dispatch_sync(self.concurrent_queue, ^{
obj = [self.dataCenterDic objectForKey:key];
});
return obj;
}
#pragma mark - 写数据
- (void)rf_setObject:(id)obj forKey:(NSString *)key{
// 异步栅栏调用设置数据:
dispatch_barrier_async(self.concurrent_queue, ^{
[self.dataCenterDic setObject:obj forKey:key];
});
}
@end