(atomic是只对set加锁,对get不加锁,所以在获取的时候会有误差)
synchronized的使用形式是
@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)直接调用这个类
SynchronizObj *obj = [[SynchronizObj alloc] init];
[obj synchronizMethod];
[obj normalMethod];
得到的结果是
19:31:09.855 LockDemo[1120:135454] synchronizMethod
19:31:14.857 LockDemo[1120:135454] normalMethod
相差了5秒执行.
(2)我们把这个放到异步执行里面看一下如何
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的使用步骤是
- lock the condition
- while (!(boolean_predicate)) {
- wait on condition
- }
- do protected work
- (optionally, signal or broadcast the condition again or change a predicate value)
- unlock the condition
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的执行。