对象锁:代码为[临界区:共同访问一段代码]synchronized(Object)语句指定的对象进行加锁
示例:
//主线程中
NSLock *lock = [[NSLock alloc] init];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
NSLog(@"线程1");
sleep(2);
[lock unlock];
NSLog(@"线程1解锁成功");
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);//以保证让线程2的代码后执行
[lock lock];
NSLog(@"线程2");
[lock unlock];
});
2016-08-19 14:23:09.659 ThreadLockControlDemo[1754:129663] 线程1
2016-08-19 14:23:11.663 ThreadLockControlDemo[1754:129663] 线程1解锁成功
2016-08-19 14:23:11.665 ThreadLockControlDemo[1754:129659] 线程2
NSLock的执行原理:
线程 1 中的 lock 锁上了,所以线程 2 中的 lock 加锁失败,阻塞线程 2,但 2 s 后线程 1 中的 lock 解锁,线程 2 就立即加锁成功,执行线程 2 中的后续代码。
查到的资料显示互斥锁会使得线程阻塞,阻塞的过程又分两个阶段,第一阶段是会先空转,可以理解成跑一个 while 循环,不断地去申请加锁,在空转一定时间之后,线程会进入 waiting 状态,此时线程就不占用CPU资源了,等锁可用的时候,这个线程会立即被唤醒。
所以如果将上面线程 1 中的 sleep(2); 改成 sleep(10); 输出的结果会变成
2016-08-19 14:25:16.226 ThreadLockControlDemo[1773:131824] 线程1
2016-08-19 14:25:26.231 ThreadLockControlDemo[1773:131831] 线程2
2016-08-19 14:25:26.231 ThreadLockControlDemo[1773:131824] 线程1解锁成功
从上面的两个输出结果可以看出,线程 2 lock 的第一秒,是一直在轮询请求加锁的,因为轮询有时间间隔,所以 ”线程 2“ 的输出晚于 ”线程 1 解锁成功“,但线程 2 lock 的第九秒,是当锁可用的时候,立即被唤醒,所以 ”线程 2“ 的输出早于 ”线程 1 解锁成功“。多做了几次试验,发现轮询 1 秒之后,线程会进入 waiting 状态。
//主线程中
NSLock *lock = [[NSLock alloc] init];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
NSLog(@"线程1");
sleep(10);
[lock unlock];
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);//以保证让线程2的代码后执行
if ([lock tryLock]) { //尝试加锁
NSLog(@"线程2");
[lock unlock];
} else {
NSLog(@"尝试加锁失败");
}
});
2016-08-19 11:42:54.433 ThreadLockControlDemo[1256:56857] 线程1
2016-08-19 11:42:55.434 ThreadLockControlDemo[1256:56861] 尝试加锁失败
由上面的结果可得知,tryLock 并不会阻塞线程。[lock tryLock] 能加锁返回 YES,不能加锁返回 NO,然后都会执行后续代码。
如果将 [lock tryLock] 替换成
[lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:10]
的话,则会返回 YES,输出 “线程 2“,lockBeforeDate: 方法会在所指定 Date 之前尝试加锁,会阻塞线程,如果在指定时间之前都不能加锁,则返回 NO,指定时间之前能加锁,则返回 YES。
@protocol NSLocking
- (void)lock;//加锁
- (void)unlock;//解锁
@end
@interface NSLock : NSObject <NSLocking> {
@private
void *_priv;
}
//尝试加锁,不会阻塞线程。true则加锁成功,false则失败,说明其他线程在加锁中这个方法无论如何都会立即返回。
- (BOOL)tryLock;
//尝试在指定NSDate之前加锁,会阻塞线程。true则加锁成功,false则失败,说明其他线程在加锁中这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。
- (BOOL)lockBeforeDate:(NSDate *)limit;
//name 是用来标识用的
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
网友评论