锁的意义就是为了防止在多线程(多任务)的情况下对共享资源(临界资源)的脏读或者脏写。也可以理解为:用于执行多线程操作时强行限制资源访问的同步机制,即并发控制中保证互斥的要求,可以理解成它用于排除并发的一种策略。
分类方式 | 分类 |
---|---|
按锁的粒度划分 | 表级锁、行级锁、页级锁 |
按锁的级别划分 | 共享锁、排他锁 |
按加锁方式划分 | 自动锁、显示锁 |
按锁的使用方式划分 | 乐观锁、悲观锁 |
按操作划分 | DML锁、DDL锁 |
等等。。 | 等等。。 |
方式1 使用NSLock类
- 该类分成了几个子类:NSLock、NSConditionLock(条件锁)、NSRecursiveLock(递归锁)以及NSCondition。
- 类内方法:
tryLock
:作用是尝试获取一个锁,并且立刻返回Bool值,YES表示获取了锁,NO表示没有获取锁失败。
lockBeforeDate:
:作用是在某个时刻之前获取锁,如果获取成功,则返回YES,NO表示获取锁失败。
- (void)lockDemo1 {
NSLock *myLock = [[NSLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[myLock lock];
NSLog(@"执行锁1");
sleep(5);
[myLock unlock];
if ([myLock tryLock]) {
NSLog(@"可以获得锁1");
}else {
NSLog(@"不可以获得锁1");
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
if ([myLock tryLock]) {
NSLog(@"---可以获得锁2");
}else {
NSLog(@"----不可以获得所2");
}
[myLock lock];
NSLog(@"执行锁2");
[myLock unlock];
});
}
方式2 使用@synchorize
- 对于@synchorize指令中使用的testLock为该锁标示,只有标示相同的时候才满足锁的效果。它的优点是不用显式地创建锁,便可以实现锁的机制。但是它会隐式地添加异常处理程序来保护代码,该程序在抛出异常的时候自动释放锁。
- (void)lockDemo2 {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized (self) {
NSLog(@"执行锁1");
sleep(5);
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
@synchronized (self) {
NSLog(@"执行锁2");
}
});
}
方式3 使用gcd
- (void)lockDemo3 {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"执行锁1");
sleep(5);
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"执行锁2");
dispatch_semaphore_signal(semaphore);
});
}
方式4 使用phtread
- (void)lockDemo4 {
__block pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&mutex);
NSLog(@"执行锁1");
sleep(5);
pthread_mutex_unlock(&mutex);
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
pthread_mutex_lock(&mutex);
NSLog(@"执行锁2");
pthread_mutex_unlock(&mutex);
});
}
方式5 OSSpinLock
- (void)lockDemo4 {
OSSpinLock spinlock = OS_SPINLOCK_INIT;
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OSSpinLockLock(&spinlock);
NSLog(@"执行锁1");
sleep(5);
OSSpinLockUnlock(&spinlock);
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
OSSpinLockLock(&spinlock);
NSLog(@"执行锁2");
OSSpinLockUnlock(&spinlock);
});
}
同样加锁解锁次数耗时结果比较:
OSSpinlock
< pthread_mutex
< NSLock+IMP
< NSLock
< @synchronized
- 1、@synchronized
内部会创建一个异常捕获的handler和其他内部使用的锁。由于内部会添加异常处理,所以耗时。- 2、NSLock 和 NSLock+IMP
两个时间非常接近。他们是pthread mutexes
封装的,但是创建对象的时候需要额外的开销。- 3、pthread_mutex
底层的API,性能比较高,处理能力不错。- 4、gcd
系统封装的C代码效果比pthread好。- 5、OSSpinLock
自旋锁不进入内核,仅仅是重新加载。如果自旋锁占用的时间是极少的(通常是纳秒级别的)性能还是比较高的,减少了系统的直接调用和上下文的切换。(因为优先级倒置问题已经弃用,此类已经被os_unfair_lock所替)
耗时结果如下: synthorize > NSLock > pthread > gcd > os_unfair_lock >OSSPinLock