线程锁

(atomic是只对set加锁,对get不加锁,因此在获取的时候会有偏差)多线程

·@synchronized异步

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的使用步骤是

  • 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的执行。

相关文章
相关标签/搜索