控制台输出:
2017-10-19 15:07:14.872279+0800 Thread-Lock[39166:851060] 线程1加锁成功
2017-10-19 15:07:16.876108+0800 Thread-Lock[39166:851060] 线程1解锁成功
2017-10-19 15:07:17.876208+0800 Thread-Lock[39166:851052] 线程4加锁成功
2017-10-19 15:07:17.876527+0800 Thread-Lock[39166:851052] 线程4解锁成功
- (void)nslock {
NSLock * cjlock = [NSLock new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[cjlock lock];
NSLog(@"线程1加锁成功");
sleep(2);
[cjlock unlock];
NSLog(@"线程1解锁成功");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([cjlock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]) {
NSLog(@"线程5加锁成功");
[cjlock unlock];
NSLog(@"线程5解锁成功");
}else {
NSLog(@"线程5加锁失败");
}
});
}
控制台输出:
2017-10-19 15:08:39.705131+0800 Thread-Lock[39204:852782] 线程1加锁成功
2017-10-19 15:08:41.708717+0800 Thread-Lock[39204:852782] 线程1解锁成功
2017-10-19 15:08:41.708717+0800 Thread-Lock[39204:852784] 线程5加锁成功
2017-10-19 15:08:41.708935+0800 Thread-Lock[39204:852784] 线程5解锁成功
注意:lock与unlock操作必须在同一线程,否则结果不确定甚至会引起死锁
由以上内容总结:
-
除 lock 和 unlock 方法外,NSLock 还提供了 tryLock 和 lockBeforeDate:两个方法。
由上面的结果可以看到 tryLock 并不会阻塞线程,[cjlock tryLock] 能加锁返回 YES,不能加锁返回 NO,然后都会执行后续代码。
这里顺便提一下 trylock 和 lock 使用场景:当前线程锁失败,也可以继续其它任务,用 trylock 合适;当前线程只有锁成功后,才会做一些有意义的工作,那就 lock,没必要轮询 trylock。以下的锁都是这样。
lockBeforeDate: 方法会在所指定 Date 之前尝试加锁,会阻塞线程,如果在指定时间之前都不能加锁,则返回 NO,指定时间之前能加锁,则返回 YES。
由于是互斥锁,当一个线程进行访问的时候,该线程获得锁,其他线程进行访问的时候,将被操作系统挂起,直到该线程释放锁,其他线程才能对其进行访问,从而却确保了线程安全。但是如果连续锁定两次,则会造成死锁问题。
2.2 NSRecursiveLock
NSRecursiveLock 是递归锁,顾名思义,可以被一个线程多次获得,而不会引起死锁。它记录了成功获得锁的次数,每一次成功的获得锁,必须有一个配套的释放锁和其对应,这样才不会引起死锁。NSRecursiveLock 会记录上锁和解锁的次数,当二者平衡的时候,才会释放锁,其它线程才可以上锁成功。
源码内容:
@interface NSRecursiveLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end










