(atomic是只对set加锁,对get不加锁,因此在获取的时候会有偏差)多线程
synchronized的使用形式是async
@synchronized (object) { //须要保护的代码块 }
设计的一个类测试
@interface SynchronizObj : NSObject - (void)synchronizMethod; - (void)normalMethod; @end @implementation SynchronizObj - (void)synchronizMethod { @synchronized (self) { NSLog(@"synchronizMethod"); sleep(5); } } - (void)testLock { static NSObject *lock = nil; if (!lock) { lock = [[NSString alloc] init]; } @synchronized(lock){ NSLog(@"开始枷锁"); sleep(5); } } - (void)normalMethod { NSLog(@"normalMethod"); } @end
(1)直接调用这个类atom
SynchronizObj *obj = [[SynchronizObj alloc] init]; [obj synchronizMethod]; [obj normalMethod];
获得的结果是spa
19:31:09.855 LockDemo[1120:135454] synchronizMethod 19:31:14.857 LockDemo[1120:135454] normalMethod
相差了5秒执行.操作系统
(2)咱们把这个放到异步执行里面看一下如何.net
dispatch_async(dispatch_get_main_queue(), ^{ [obj testLock]; }); dispatch_async(dispatch_get_main_queue(), ^{ sleep(1);//让synchronizMethod先执行 [obj testLock]; }); 结果 22:12:21.745 LockDemo[1144:141456] 开始枷锁 22:12:27.889 LockDemo[1144:141446] 开始枷锁
放到异步里,只相差6秒执行,能够看到,testLock中@synchronized保护的代码块,在未返回的状况下,其余线程是没法访问。线程
(3)@synchronized须要一个参数,若是传递的是不一样的参数呢?咱们把测试的类的代码修改一下设计
- (void)synchronizMethod:(id)obj; - (void)normalMethod:(id)obj; - (void)synchronizMethod:(id)obj { @synchronized (self) { NSLog(@"synchronizMethod:%@", obj); sleep(5); } } - (void)normalMethod:(id)obj { @synchronized (self) { NSLog(@"normalMethod"); } }
调用代码和结果
SynchronizObj *obj = [[SynchronizObj alloc] init]; SynchronizObj *obj1 = [[SynchronizObj alloc] init]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [obj synchronizMethod:obj]; }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1);//让obj synchronizMethod先执行 [obj normalMethod:obj]; }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1);//让obj synchronizMethod先执行 [obj1 synchronizMethod:obj]; }); 结果 20:05:46.491 LockDemo[1199:156733] synchronizMethod:<SynchronizObj: 0x7fd632f0b000> 20:05:47.492 LockDemo[1199:156740] synchronizMethod:<SynchronizObj: 0x7fd632f0b000> 20:05:51.497 LockDemo[1199:156744] normalMethod
能够看到,多线程中,@synchronized获取的是不一样的参数,是不会阻塞的,但若是是同一个参数,则只有一个会得到锁,直到锁保护的内容执行完毕。
·NSLock
NSLock等是实现了NSLocking协议,因此具有了lock和unlock这一对上锁解锁的方法,这一对方法须要在同一线程中,不然会致使报错(先调用了unlock,没有或者后调用了lock)或者没法持有锁(上一个lock未调用unlock)。
通常是这样调用
NSLock* lock = [[NSLock alloc] init]; dispatch_async(dispatch_get_main_queue(), ^{ [lock lock]; NSLog(@"任务1"); sleep(1); [lock unlock]; }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1);// 让任务1先执行 [lock lock]; NSLog(@"任务2"); [lock unlock]; }); 结果 20:23:43.869 LockDemo[1263:169163] 任务1 20:23:44.870 LockDemo[1263:169200] 任务2
·NSConditionLock
NSConditionLock顾名思义,叫作条件锁,根据某种条件进行上锁解锁,但这个条件,必须是个NSInteger变量。
NSConditionLock有这么几个特别的地方:
- (instancetype)initWithCondition:(NSInteger)condition; @property (readonly) NSInteger condition; - (void)lockWhenCondition:(NSInteger)condition; - (void)unlockWithCondition:(NSInteger)condition;
NSConditionLock有个condition属性,只读的,使用上面两个方法进行上锁和解锁的时候,lockWhenCondition时,会将参数condition和自身的condition属性进行比较,若是相同,才能进行上锁操做,不然会处于等待;unlockWithCondition时,会将参数condition自动赋值给自身的condition属性,用于以后的对比。一样,调用initWithCondition初始化,也会将condition赋值。
文档中用生产和消费举例,下面是个人测试代码和结果
NSConditionLock* lock = [[NSConditionLock alloc] init]; NSMutableArray* products = [NSMutableArray array]; #define hasData YES #define noData NO dispatch_async(dispatch_get_main_queue(), ^{ for (int i = 0; i < 3; i ++) { [lock lockWhenCondition:noData]; [products addObject:@"product"]; NSLog(@"生产产品%d condition:%ld", i, lock.condition); [lock unlockWithCondition:hasData]; } }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1);// 让任务1先执行 for (int i = 0; i < 3; i ++) { [lock lockWhenCondition:hasData]; [products removeAllObjects]; NSLog(@"消费产品%d condition:%ld", i, lock.condition); [lock unlockWithCondition:noData]; } }); 结果 20:45:59.579 LockDemo[1297:184273] 生产产品0 condition:0 20:46:00.580 LockDemo[1297:184322] 消费产品0 condition:1 20:46:00.580 LockDemo[1297:184273] 生产产品1 condition:0 20:46:00.581 LockDemo[1297:184322] 消费产品1 condition:1 20:46:00.581 LockDemo[1297:184273] 生产产品2 condition:0 20:46:00.581 LockDemo[1297:184322] 消费产品2 condition:1
·NSRecursiveLock
NSRecursiveLock,递归锁,容许在同一线程中屡次持有锁,但每次持有必须配对解锁。
@property (nonatomic, strong) NSRecursiveLock* lock; - (void)testMethod { _lock = [[NSRecursiveLock alloc] init]; [self testRecursiveLock:5]; } - (void)testRecursiveLock:(NSInteger)value { [_lock lock]; if (value > 0) { NSLog(@"第%ld次持有锁", 6 - value); -- value; [self testRecursiveLock:value]; } [_lock unlock]; } 结果 21:06:27.322 LockDemo[1327:190602] 第1次持有锁 21:06:27.323 LockDemo[1327:190602] 第2次持有锁 21:06:27.323 LockDemo[1327:190602] 第3次持有锁 21:06:27.323 LockDemo[1327:190602] 第4次持有锁 21:06:27.323 LockDemo[1327:190602] 第5次持有锁
不一样线程持有锁,按照基本状况,即先持有锁的线程先执行,直到最终释放锁。好比对上面的代码作修改,在递归期间,有另外一线程请求锁
- (void)testMethod { _lock = [[NSRecursiveLock alloc] init]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1);// 让递归程序先执行 [_lock lock]; NSLog(@"异步加锁"); sleep(5); NSLog(@"异步解锁"); [_lock unlock]; }); [self testRecursiveLock:5]; } - (void)testRecursiveLock:(NSInteger)value { [_lock lock]; if (value > 0) { NSLog(@"第%ld次持有锁", 6 - value); sleep(1); -- value; [self testRecursiveLock:value]; } [_lock unlock]; } 结果 21:12:03.876 LockDemo[1357:194699] 第1次持有锁 21:12:04.877 LockDemo[1357:194699] 第2次持有锁 21:12:05.878 LockDemo[1357:194699] 第3次持有锁 21:12:06.879 LockDemo[1357:194699] 第4次持有锁 21:12:07.879 LockDemo[1357:194699] 第5次持有锁 21:12:08.881 LockDemo[1357:194735] 异步加锁 21:12:13.884 LockDemo[1357:194735] 异步解锁
上面的测试代码,先在主线程持有递归锁,其余线程以后请求锁的时候,须要等待主线程的递归锁把锁保护内容执行完毕,即递归调用完成,而不是在某一次的unlock后就得到锁资源。
·NSCondition
NSCondition看起来和NSConditionLock很像,但其实差异仍是挺大的。
文档中推荐NSCondition的使用步骤是
NSCondition的使用和POSIX很相似,都须要加(推荐加)boolean_predicate来确保这个过程。由于操做系统的设计,容许NSCondition返回一个虚假的成功信号,在没有触发你的代码状况下,唤醒NSCondition锁住的线程。
测试代码以下
@property (atomic, assign) BOOL ready_to_go; NSCondition* condition = [[NSCondition alloc] init]; _ready_to_go = NO; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [condition lock]; if (_ready_to_go == NO) { NSLog(@"等待1"); [condition wait]; } NSLog(@"执行1"); [condition unlock]; }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(3); [condition lock]; _ready_to_go = YES; NSLog(@"执行2"); [condition broadcast];//触发全部等待condition的线程 // 也可使用这个[condition signal]; [condition unlock]; }); 结果 21:48:04.434 LockDemo[1392:217171] 等待1 21:48:07.435 LockDemo[1392:217182] 执行2 21:48:07.435 LockDemo[1392:217171] 执行1
虽然任务1先执行,但因为标识不符合,因此进入等待,任务2执行后,更新标识,才触发到任务1的执行。