iOS-锁的原理分析(二)

前言

iOS-锁的原理分析(一)我们分析了synchronized锁,这篇文章我们继续介绍其它的锁。

1 锁的分类

在我们项目应用中, 除了synchronized以外,我们也使用过其它的类,比如NSLockNSConditionNSConditionLockNSRecursiveLock等之类的锁,那么在我们iOS中,锁到底有几种呢?我们来看下。
其实在iOS中分为两大种锁,自旋锁互斥锁
互斥锁是⼀种⽤于多线程编程中,防⽌两条线程同时对同⼀公共资源(⽐如全局变量)进行读写的的机制,其目地通过将代码切片成一个一个的临界区而达成。它是一种闲等待,这里又分递归和不递归
比如像:
- NSLock
- pthread_mutex
- synchronized
⾃旋锁:线程反复检查锁变量是否可⽤。由于线程在这⼀过程中保持执⾏,因此是⼀种忙等待。⼀旦获取了⾃旋锁,线程会⼀直保持该锁,直⾄显式释放⾃旋锁。⾃旋锁避免了进程上下⽂的调度开销,因此对于线程只会阻塞很短时间的场合是有效的。

2 NSLock和NSRecursiveLock的分析

我们先来看下以下代码:

- (void)ro_crash{
    NSLog(@"robert");
    for (int i = 0; i < 200000; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            _testArray = [NSMutableArray array];
        });
    }
}

这里多线程中_testArray会出现崩溃,这里加锁处理就可以解决。

  dispatch_async(dispatch_get_global_queue(0, 0), ^{
        static void (^testMethod)(int);
        testMethod = ^(int value){
            if (value > 0) {
                NSLog(@"current value = %d",value);
                testMethod(value - 1);
            }
        };
        testMethod(10);
    });

我们运行一下,看下效果,如图:


1

这是按顺序打印出来了value值,如果我们加一个循环会如何,代码如下:

  NSLock *lock = [[NSLock alloc] init];
    for (int i= 0; i<10; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            static void (^testMethod)(int);
            testMethod = ^(int value){
                if (value > 0) {
                    NSLog(@"current value = %d",value);
                    testMethod(value - 1);
                }
            };
            testMethod(10);
        });
    }

它的运行效果,如图:


2

这里数据出问题,这是多线程引线的线程不安全,所以这里需要加锁处理,那么这么锁加在哪里呢?我们来看下。

 NSLock *lock = [[NSLock alloc] init];
    for (int i= 0; i<10; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            static void (^testMethod)(int);
            [lock lock];
            testMethod = ^(int value){
                if (value > 0) {
                    NSLog(@"current value = %d",value);
                    testMethod(value - 1);
                }
            };
           
            testMethod(10);
            [lock unlock];
        });
    }

像这样加锁可以解决,或者在

 [lock lock];
testMethod(10);
[lock unlock];

也可以解决。
如果我们把代码改成以下方式:

 NSLock *lock = [[NSLock alloc] init];
    for (int i= 0; i<10; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            static void (^testMethod)(int);
            testMethod = ^(int value){
                [lock lock];
                if (value > 0) {
                    NSLog(@"current value = %d",value);
                    testMethod(value - 1);
                }
            };
           
            testMethod(10);
            [lock unlock];
        });
    }

经过测试是不可以的,造成类似死锁,在testMethod中,testMethod(value - 1);这行代码会在进入testMethod中,又加锁一次,不断的递归加锁,这时候递归加锁出现的问题,这也证实了NSLock是非递归的,无法还原出来,造成了类似死锁的现象,当然我们可以通过synchronized来解决。这次我们不用它,我们用NSRecursiveLock来试下,代码如下:

 self.recursiveLock = [[NSRecursiveLock alloc] init];
for (int i= 0; i<10; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            static void (^testMethod)(int);
            testMethod = ^(int value){
                [self.recursiveLock lock];
                if (value > 0) {
                    NSLog(@"current value = %d",value);
                    testMethod(value - 1);
                }
            };
           
            testMethod(10);
            [self.recursiveLock unlock];
        });
    }

其结果如图:

2

上图也说明了执行没有问题,说明NSRecursiveLock是递归的,但是我们有testMethod(10); 这行代码,只打印一次的输出,为什么呢,因为iOS-锁的原理分析(一)这篇文章介绍过,递归锁是无法对多线程起作用的,我们可以用synchronized这把锁解决,因为它解决了可多线程。

3 NSCondition的分析

我们先把代码放上来。
RoLock.h代码:

#import 

NS_ASSUME_NONNULL_BEGIN

@interface RoLock : NSObject
@property (nonatomic, copy) NSThread *thread;
@property (nonatomic, assign) int value;
@property (nonatomic, assign) int condition;
- (void)mylockWithCondition:(int)condition;
- (void)myUnlockWithCondition:(int)condition;
@end

NS_ASSUME_NONNULL_END

RoLock.m代码:

#import "RoLock.h"

@interface RoLock ()
@property (nonatomic, strong) NSCondition *testCondition;
@end

@implementation RoLock

- (instancetype)init
{
    self = [super init];
    if (self) {
        _testCondition = [NSCondition new];
        _value = 2;
    }
    return self;
}

- (void)mylockWithCondition:(int)condition {
    [_testCondition lock];
    while (_thread != nil || _value != condition) {
        if (![_testCondition waitUntilDate:[NSDate distantPast]]) {
            [_testCondition unlock];
        }
    }
    _thread = [NSThread currentThread];
    [_testCondition unlock];
}

- (void)myUnlockWithCondition:(int)condition {
    _value = condition;
    [_testCondition lock];
    _thread = nil;
    [_testCondition broadcast];
    [_testCondition unlock];
}
@end

ViewController.m代码:

#import "ViewController.h"
#import "RoLock.h"

@interface ViewController ()
@property (nonatomic, assign) NSUInteger ticketCount;
@property (nonatomic, strong) NSCondition *testCondition;
@property (nonatomic, strong) RoLock *myLock;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.ticketCount = 0;
    self.myLock = [[RoLock alloc] init];
    [self ro_testConditon];

}

#pragma mark -- NSCondition

- (void)ro_testConditon{
    
    _testCondition = [[NSCondition alloc] init];
    //创建生产-消费者
    for (int i = 0; i < 50; i++) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            [self ro_producer];
        });
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            [self ro_consumer];
        });
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            [self ro_consumer];
        });
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            [self ro_producer];
        });
    }
}

- (void)ro_producer{
    [_testCondition lock]; // 操作的多线程影响
    self.ticketCount = self.ticketCount + 1;
    NSLog(@"生产一个 现有 count %zd",self.ticketCount);
    [_testCondition signal]; // 信号
    [_testCondition unlock];
}

- (void)ro_consumer{
 
     [_testCondition lock];  // 操作的多线程影响
    if (self.ticketCount == 0) {
        NSLog(@"等待 count %zd",self.ticketCount);
        [_testCondition wait];
    }
    //注意消费行为,要在等待条件判断之后
    self.ticketCount -= 1;
    NSLog(@"消费一个 还剩 count %zd ",self.ticketCount);
     [_testCondition unlock];
}

#pragma mark -- NSConditionLock
- (void)ro_testConditonLock{
    
    /**
     1: NSConditionLock VS NSCondition
     2: 2 -> 是什么东西
     3: lockWhenCondition -> 如何控制
     4: unlockWithCondition 又做了什么?
     */

    NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:2];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        [conditionLock lockWhenCondition:1];
        NSLog(@"线程 1");
        [conditionLock unlockWithCondition:0];
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        [conditionLock lockWhenCondition:2];
        sleep(0.1);
        NSLog(@"线程 2");
        [conditionLock unlockWithCondition:1];
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
       [conditionLock lock];
       NSLog(@"线程 3");
       [conditionLock unlock];
    });
    //
}

我们接下来分析NSCondition这个锁。
NSCondition的对象实际上作为⼀个锁和⼀个线程检查器:锁主要为了当检测条件时保护数据源,执⾏条件引发的任务;线程检查器,主要是根据条件决定是否继续运⾏线程,即线程是否被阻塞。

  • [condition lock]; ⼀般⽤于多线程同时访问、修改同⼀个数据源,保证在时间内数据源只被访问、修改⼀次,其他线程的命令需要在lock 外等待,只到unlock ,才可访问
  • [condition unlock]; 与lock 同时使⽤
  • [condition wait]; 让当前线程处于等待状态
  • [condition signal]; cpu发信号告诉线程不⽤在等待,可以继续执⾏
    这里我们不加锁的情况如图:
2021-08-19 12:32:31.571513+0800 004-NSCondition[61699:2558417] 等待 count 0
2021-08-19 12:32:31.571514+0800 004-NSCondition[61699:2558419] 生产一个 现有 count 1
2021-08-19 12:32:31.571537+0800 004-NSCondition[61699:2558415] 消费一个 还剩 count 0 
2021-08-19 12:32:31.571514+0800 004-NSCondition[61699:2558418] 生产一个 现有 count 1
2021-08-19 12:32:31.571739+0800 004-NSCondition[61699:2558417] 消费一个 还剩 count 0 
2021-08-19 12:32:31.571765+0800 004-NSCondition[61699:2558419] 生产一个 现有 count 1
2021-08-19 12:32:31.571791+0800 004-NSCondition[61699:2558415] 消费一个 还剩 count 0 
2021-08-19 12:32:31.571819+0800 004-NSCondition[61699:2558418] 等待 count 0
2021-08-19 12:32:31.571867+0800 004-NSCondition[61699:2558419] 生产一个 现有 count 1
2021-08-19 12:32:31.571871+0800 004-NSCondition[61699:2558417] 生产一个 现有 count 2
2021-08-19 12:32:31.571963+0800 004-NSCondition[61699:2558415] 消费一个 还剩 count 1 
2021-08-19 12:32:31.572315+0800 004-NSCondition[61699:2558420] 消费一个 还剩 count 0 
2021-08-19 12:32:31.572669+0800 004-NSCondition[61699:2558416] 生产一个 现有 count 1
2021-08-19 12:32:31.572747+0800 004-NSCondition[61699:2558418] 消费一个 还剩 count 0 
2021-08-19 12:32:31.574770+0800 004-NSCondition[61699:2558419] 生产一个 现有 count 1
2021-08-19 12:32:31.574952+0800 004-NSCondition[61699:2558415] 消费一个 还剩 count 0 
2021-08-19 12:32:31.575127+0800 004-NSCondition[61699:2558417] 等待 count 0
2021-08-19 12:32:31.575247+0800 004-NSCondition[61699:2558420] 生产一个 现有 count 1
2021-08-19 12:32:31.575573+0800 004-NSCondition[61699:2558418] 生产一个 现有 count 2
2021-08-19 12:32:31.575933+0800 004-NSCondition[61699:2558419] 消费一个 还剩 count 1 
2021-08-19 12:32:31.576146+0800 004-NSCondition[61699:2558417] 消费一个 还剩 count 0 
2021-08-19 12:32:31.576446+0800 004-NSCondition[61699:2558416] 等待 count 0
2021-08-19 12:32:31.576768+0800 004-NSCondit2021-08-19 12:32:31.576974+0800 004-NSCondition[61699:2558416] 消费一个 还剩 count 0 
ion[61699:2558415] 生产一个 现有 count 1
2021-08-19 12:32:31.577360+0800 004-NSCondition[61699:2558419] 消费一个 还剩 count 0 
2021-08-19 12:32:31.577156+0800 004-NSCondition[61699:2558417] 生产一个 现有 count 1
2021-08-19 12:32:31.577567+0800 004-NSCondition[61699:2558418] 等待 count 0
2021-08-19 12:32:31.577779+0800 004-NSCondition[61699:2558420] 生产一个 现有 count 1
2021-08-19 12:32:31.578487+0800 004-NSCondition[61699:2558415] 生产一个 现有 count 2
2021-08-19 12:32:31.578681+0800 004-NSCondition[61699:2558428] 消费一个 还剩 count 1 
2021-08-19 12:32:31.578817+0800 004-NSCondition[61699:2558429] 消费一个 还剩 count 0 
2021-08-19 12:32:31.578941+0800 004-NSCondition[61699:2558416] 生产一个 现有 count 1
2021-08-19 12:32:31.579155+0800 004-NSCondition[61699:2558419] 生产一个 现有 count 2
2021-08-19 12:32:31.579322+0800 004-NSCondition[61699:2558415] 消费一个 还剩 count 1 
2021-08-19 12:32:31.580203+0800 004-NSCondition[61699:2558418] 消费一个 还剩 count 0 
2021-08-19 12:32:31.580384+0800 004-NSCondition[61699:2558417] 等待 count 0
2021-08-19 12:32:31.580626+0800 004-NSCondition[61699:2558416] 生产一个 现有 count 1
2021-08-19 12:32:31.580901+0800 004-NSCondition[61699:2558428] 生产一个 现有 count 2
2021-08-19 12:32:31.581122+0800 004-NSCondition[61699:2558415] 消费一个 还剩 count 1 
2021-08-19 12:32:31.581326+0800 004-NSCondition[61699:2558419] 消费一个 还剩 count 0 
2021-08-19 12:32:31.581727+0800 004-NSCondition[61699:2558415] 生产一个 现有 count 2
2021-08-19 12:32:31.581579+0800 004-NSCondition[61699:2558429] 生产一个 现有 count 1
2021-08-19 12:32:31.586054+0800 004-NSCondition[61699:2558432] 生产一个 现有 count 1
2021-08-19 12:32:31.585909+0800 004-NSCondition[61699:2558431] 等待 count 0
2021-08-19 12:32:31.582528+0800 004-NSCondition[61699:2558417] 消费一个 还剩 count 0 
2021-08-19 12:32:31.582353+0800 004-NSCondition[61699:2558416] 生产一个 现有 count 1
2021-08-19 12:32:31.585832+0800 004-NSCondition[61699:2558430] 消费一个 还剩 count 0 
2021-08-19 12:32:31.582138+0800 004-NSCondition[61699:2558428] 消费一个 还剩 count 0 
2021-08-19 12:32:31.582755+0800 004-NSCondition[61699:2558418] 生产一个 现有 count 1
2021-08-19 12:32:31.581933+0800 004-NSCondition[61699:2558420] 消费一个 还剩 count 1 
2021-08-19 12:32:31.590021+0800 004-NSCondition[61699:2558419] 生产一个 现有 count 2
2021-08-19 12:32:31.590240+0800 004-NSCondition[61699:2558415] 消费一个 还剩 count 1 
2021-08-19 12:32:31.590317+0800 004-NSCondition[61699:2558429] 消费一个 还剩 count 0 
2021-08-19 12:32:31.590954+0800 004-NSCondition[61699:2558432] 生产一个 现有 count 1
2021-08-19 12:32:31.592834+0800 004-NSCondition[61699:2558431] 消费一个 还剩 count 0 
2021-08-19 12:32:31.594283+0800 004-NSCondition[61699:2558419] 生产一个 现有 count 1
2021-08-19 12:32:31.596020+0800 004-NSCondition[61699:2558415] 等待 count 0
2021-08-19 12:32:31.595991+0800 004-NSCondition[61699:2558429] 消费一个 还剩 count 0 
2021-08-19 12:32:31.596064+0800 004-NSCondition[61699:2558432] 生产一个 现有 count 1
2021-08-19 12:32:31.596265+0800 004-NSCondition[61699:2558417] 生产一个 现有 count 2
2021-08-19 12:32:31.596612+0800 004-NSCondition[61699:2558428] 消费一个 还剩 count 1 
2021-08-19 12:32:31.596658+0800 004-NSCondition[61699:2558430] 消费一个 还剩 count 0 
2021-08-19 12:32:31.596851+0800 004-NSCondition[61699:2558431] 生产一个 现有 count 1
2021-08-19 12:32:31.596938+0800 004-NSCondition[61699:2558416] 生产一个 现有 count 2
2021-08-19 12:32:31.597300+0800 004-NSCondition[61699:2558419] 消费一个 还剩 count 1 
2021-08-19 12:32:31.597420+0800 004-NSCondition[61699:2558415] 消费一个 还剩 count 0 
2021-08-19 12:32:31.597544+08002021-08-19 12:32:31.597765+0800 004-NSCondition[61699:2558420] 生产一个 现有 count 1
2021-08-19 12:32:31.597980+0800 004-NSCondition[61699:2558418] 生产一个 现有 count 2
2021-08-19 12:32:31.598215+0800 004-NSCondition[61699:2558415] 消费一个 还剩 count 1 
2021-08-19 12:32:31.598425+0800 004-NSCondition[61699:2558431] 消费一个 还剩 count 0 
2021-08-19 12:32:31.598628+0800 004-NSCondition[61699:2558417] 生产一个 现有 count 1
2021-08-19 12:32:31.598916+0800 004-NSCondition[61699:2558428] 生产一个 现有 count 2
2021-08-19 12:32:31.599191+0800 004-NSCondition[61699:2558419] 消费一个 还剩 count 1 
2021-08-19 12:32:31.599271+0800 004-NSCondition[61699:2558436] 消费一个 还剩 count 0 
2021-08-19 12:32:31.599403+0800 004-NSCondition[61699:2558437] 生产一个 现有 count 1
2021-08-19 12:32:31.599505+0800 004-NSCondition[61699:2558416] 生产一个 现有 count 2
2021-08-19 12:32:31.599829+0800 004-NSCondition[61699:2558430] 消费一个 还剩 count 1 
2021-08-19 12:32:31.600045+0800 004-NSCondition[61699:2558429] 消费一个 还剩 count 0 
2021-08-19 12:32:31.603315+0800 004-NSCondition[61699:2558438] 消费一个 还剩 count 0 
2021-08-19 12:32:31.603430+0800 004-NSCondition[61699:2558439] 生产一个 现有 count 1
2021-08-19 12:32:31.603631+0800 004-NSCondition[61699:2558440] 生产一个 现有 count 1
2021-08-19 12:32:31.604509+0800 004-NSCondition[61699:2558441] 消费一个 还剩 count 0 
2021-08-19 12:32:31.604668+0800 004-NSCondition[61699:2558442] 生产一个 现有 count 1
2021-08-19 12:32:31.604820+0800 004-NSCondition[61699:2558443] 生产一个 现有 count 2
2021-08-19 12:32:31.604892+0800 004-NSCondition[61699:2558444] 消费一个 还剩 count 1 
2021-08-19 12:32:31.604991+0800 004-NSCondition[61699:2558445] 消费一个 还剩 count 0 
2021-08-19 12:32:31.605167+0800 004-NSCondition[61699:2558446] 生产一个 现有 count 1
2021-08-19 12:32:31.607993+0800 004-NSCondition[61699:2558448] 消费一个 还剩 count 0 
2021-08-19 12:32:31.614513+0800 004-NSCondition[61699:2558450] 生产一个 现有 count 2
2021-08-19 12:32:31.608386+0800 004-NSCondition[61699:2558447] 生产一个 现有 count 1
2021-08-19 12:32:31.617526+0800 004-NSCondition[61699:2558452] 消费一个 还剩 count 1 
2021-08-19 12:32:31.615824+0800 004-NSCondition[61699:2558449] 消费一个 还剩 count 1 
2021-08-19 12:32:31.617554+0800 004-NSCondition[61699:2558456] 生产一个 现有 count 2
2021-08-19 12:32:31.617563+0800 004-NSCondition[61699:2558457] 消费一个 还剩 count 0 
2021-08-19 12:32:31.617662+0800 004-NSCondition[61699:2558459] 生产一个 现有 count 2
 004-NSCondition[61699:2558432] 等待 count 0
2021-08-19 12:32:31.617661+0800 004-NSCondition[61699:2558458] 生产一个 现有 count 1
2021-08-19 12:32:31.618536+0800 004-NSCondition[61699:2558415] 消费一个 还剩 count 1 
2021-08-19 12:32:31.618826+0800 004-NSCondition[61699:2558431] 消费一个 还剩 count 0 
2021-08-19 12:32:31.619701+0800 004-NSCondition[61699:2558418] 生产一个 现有 count 1
2021-08-19 12:32:31.620013+0800 004-NSCondition[61699:2558430] 生产一个 现有 count 2
2021-08-19 12:32:31.620339+0800 004-NSCondition[61699:2558437] 消费一个 还剩 count 1 
2021-08-19 12:32:31.620765+0800 004-NSCondition[61699:2558461] 生产一个 现有 count 1
2021-08-19 12:32:31.620832+0800 004-NSCondition[61699:2558460] 消费一个 还剩 count 0 
2021-08-19 12:32:31.621156+0800 004-NSCondition[61699:2558436] 消费一个 还剩 count 0 
2021-08-19 12:32:31.621179+0800 004-NSCondition[61699:2558462] 生产一个 现有 count 1
2021-08-19 12:32:31.621463+0800 004-NSCondition[61699:2558420] 消费一个 还剩 count 0 
2021-08-19 12:32:31.622799+0800 004-NSCondition[61699:2558428] 生产一个 现有 count 1
2021-08-19 12:32:31.623678+0800 004-NSCondition[61699:2558464] 生产一个 现有 count 2
2021-08-19 12:32:31.623920+0800 004-NSCondition[61699:2558466] 消费一个 还剩 count 1 
2021-08-19 12:32:31.624055+0800 004-NSCondition[61699:2558417] 消费一个 还剩 count 0 
2021-08-19 12:32:31.624638+0800 004-NSCondition[61699:2558420] 生产一个 现有 count 2
2021-08-19 12:32:31.624628+0800 004-NSCondition[61699:2558465] 生产一个 现有 count 1
2021-08-19 12:32:31.624716+0800 004-NSCondition[61699:2558467] 消费一个 还剩 count 1 
2021-08-19 12:32:31.625252+0800 004-NSCondition[61699:2558474] 消费一个 还剩 count 1 
2021-08-19 12:32:31.624734+0800 004-NSCondition[61699:2558468] 消费一个 还剩 count 0 
2021-08-19 12:32:31.624853+0800 004-NSCondition[61699:2558462] 生产一个 现有 count 1
2021-08-19 12:32:31.625028+0800 004-NSCondition[61699:2558469] 生产一个 现有 count 2
2021-08-19 12:32:31.625066+0800 004-NSCondition[61699:2558470] 消费一个 还剩 count 1 
2021-08-19 12:32:31.625128+0800 004-NSCondition[61699:2558471] 消费一个 还剩 count 0 
2021-08-19 12:32:31.625179+0800 004-NSCondition[61699:2558472] 生产一个 现有 count 1
2021-08-19 12:32:31.625221+0800 004-NSCondition[61699:2558473] 生产一个 现有 count 2
2021-08-19 12:32:31.625720+0800 004-NSCondition[61699:2558463] 消费一个 还剩 count 0 
2021-08-19 12:32:31.625877+0800 004-NSCondition[61699:2558475] 生产一个 现有 count 1
2021-08-19 12:32:31.626032+0800 004-NSCondition[61699:2558476] 生产一个 现有 count 2
2021-08-19 12:32:31.626065+0800 004-NSCondition[61699:2558460] 消费一个 还剩 count 1 
2021-08-19 12:32:31.626362+0800 004-NSCondition[61699:2558461] 消费一个 还剩 count 0 
2021-08-19 12:32:31.626834+0800 004-NSCondition[61699:2558465] 生产一个 现有 count 1
2021-08-19 12:32:31.627117+0800 004-NSCondition[61699:2558478] 生产一个 现有 count 1
2021-08-19 12:32:31.627128+0800 004-NSCondition[61699:2558479] 消费一个 还剩 count 0 
2021-08-19 12:32:31.627144+0800 004-NSCondition[61699:2558477] 消费一个 还剩 count 0 
2021-08-19 12:32:31.627234+0800 004-NSCondition[61699:2558480] 生产一个 现有 count 1
2021-08-19 12:32:31.627424+0800 004-NSCondition[61699:2558420] 生产一个 现有 count 2
2021-08-19 12:32:31.627809+0800 004-NSCondition[61699:2558417] 消费一个 还剩 count 1 
2021-08-19 12:32:31.628260+0800 004-NSCondition[61699:2558461] 消费一个 还剩 count 0 
2021-08-19 12:32:31.628504+0800 004-NSCondition[61699:2558476] 生产一个 现有 count 1
2021-08-19 12:32:31.628862+0800 004-NSCondition[61699:2558475] 生产一个 现有 count 2
2021-08-19 12:32:31.629417+0800 004-NSCondition[61699:2558477] 消费一个 还剩 count 1 
2021-08-19 12:32:31.629616+0800 004-NSCondition[61699:2558479] 消费一个 还剩 count 0 
2021-08-19 12:32:31.630033+0800 004-NSCondition[61699:2558472] 生产一个 现有 count 1
2021-08-19 12:32:31.630247+0800 004-NSCondition[61699:2558481] 生产一个 现有 count 2
2021-08-19 12:32:31.630305+0800 004-NSCondition[61699:2558483] 消费一个 还剩 count 1 
2021-08-19 12:32:31.630336+0800 004-NSCondition[61699:2558482] 消费一个 还剩 count 0 
2021-08-19 12:32:31.630432+0800 004-NSCondition[61699:2558484] 生产一个 现有 count 1
2021-08-19 12:32:31.630523+0800 004-NSCondition[61699:2558470] 生产一个 现有 count 2
2021-08-19 12:32:31.630960+0800 004-NSCondition[61699:2558471] 消费一个 还剩 count 1 
2021-08-19 12:32:31.631647+0800 004-NSCondition[61699:2558469] 消费一个 还剩 count 0 
2021-08-19 12:32:31.632209+0800 004-NSCondition[61699:2558482] 生产一个 现有 count 1
2021-08-19 12:32:31.632537+0800 004-NSCondition[61699:2558472] 生产一个 现有 count 2
2021-08-19 12:32:31.632867+0800 004-NSCondition[61699:2558479] 消费一个 还剩 count 1 
2021-08-19 12:32:31.633231+0800 004-NSCondition[61699:2558481] 消费一个 还剩 count 0 
2021-08-19 12:32:31.633453+0800 004-NSCondition[61699:2558486] 生产一个 现有 count 2
2021-08-19 12:32:31.633557+0800 004-NSCondition[61699:2558485] 生产一个 现有 count 1
2021-08-19 12:32:31.633761+0800 004-NSCondition[61699:2558487] 消费一个 还剩 count 0 
2021-08-19 12:32:31.633651+0800 004-NSCondition[61699:2558483] 消费一个 还剩 count 1 
2021-08-19 12:32:31.633877+0800 004-NSCondition[61699:2558488] 生产一个 现有 count 1
2021-08-19 12:32:31.634151+0800 004-NSCondition[61699:2558479] 生产一个 现有 count 2
2021-08-19 12:32:31.634313+0800 004-NSCondition[61699:2558472] 消费一个 还剩 count 1 
2021-08-19 12:32:31.634702+0800 004-NSCondition[61699:2558481] 消费一个 还剩 count 0 
2021-08-19 12:32:31.635145+0800 004-NSCondition[61699:2558482] 生产一个 现有 count 1
2021-08-19 12:32:31.635482+0800 004-NSCondition[61699:2558477] 生产一个 现有 count 2
2021-08-19 12:32:31.635839+0800 004-NSCondition[61699:2558475] 消费一个 还剩 count 1 
2021-08-19 12:32:31.636292+0800 004-NSCondition[61699:2558463] 消费一个 还剩 count 0 
2021-08-19 12:32:31.636546+0800 004-NSCondition[61699:2558489] 生产一个 现有 count 2
2021-08-19 12:32:31.636546+0800 004-NSCondition[61699:2558490] 生产一个 现有 count 1
2021-08-19 12:32:31.636866+0800 004-NSCondition[61699:2558473] 消费一个 还剩 count 1 
2021-08-19 12:32:31.636938+0800 004-NSCondition[61699:2558491] 消费一个 还剩 count 0 
2021-08-19 12:32:31.637352+0800 004-NSCondition[61699:2558481] 生产一个 现有 count 1
2021-08-19 12:32:31.637689+0800 004-NSCondition[61699:2558428] 生产一个 现有 count 2
2021-08-19 12:32:31.637912+0800 004-NSCondition[61699:2558437] 消费一个 还剩 count 1 
2021-08-19 12:32:31.638501+0800 004-NSCondition[61699:2558430] 消费一个 还剩 count 0 
2021-08-19 12:32:31.638799+0800 004-NSCondition[61699:2558468] 生产一个 现有 count 1
2021-08-19 12:32:31.639851+0800 004-NSCondition[61699:2558474] 生产一个 现有 count 2
2021-08-19 12:32:31.639993+0800 004-NSCondition[61699:2558462] 消费一个 还剩 count 1 
2021-08-19 12:32:31.642834+0800 004-NSCondition[61699:2558467] 消费一个 还剩 count 0 
2021-08-19 12:32:31.642873+0800 004-NSCondition[61699:2558468] 生产一个 现有 count 1
2021-08-19 12:32:31.643215+0800 004-NSCondition[61699:2558430] 生产一个 现有 count 2
2021-08-19 12:32:31.643502+0800 004-NSCondition[61699:2558462] 消费一个 还剩 count 1 
2021-08-19 12:32:31.643811+0800 004-NSCondition[61699:2558474] 消费一个 还剩 count 0 
2021-08-19 12:32:31.644165+0800 004-NSCondition[61699:2558437] 生产一个 现有 count 1
2021-08-19 12:32:31.644502+0800 004-NSCondition[61699:2558492] 生产一个 现有 count 2
2021-08-19 12:32:31.644558+0800 004-NSCondition[61699:2558481] 消费一个 还剩 count 1 
2021-08-19 12:32:31.644801+0800 004-NSCondition[61699:2558491] 消费一个 还剩 count 0 
2021-08-19 12:32:31.645154+0800 004-NSCondition[61699:2558418] 生产一个 现有 count 1
2021-08-19 12:32:31.645457+0800 004-NSCondition[61699:2558489] 生产一个 现有 count 2
2021-08-19 12:32:31.645743+0800 004-NSCondition[61699:2558463] 消费一个 还剩 count 1 
2021-08-19 12:32:31.646066+0800 004-NSCondition[61699:2558476] 消费一个 还剩 count 0 
2021-08-19 12:32:31.646408+0800 004-NSCondition[61699:2558461] 生产一个 现有 count 1
2021-08-19 12:32:31.646756+0800 004-NSCondition[61699:2558417] 生产一个 现有 count 2
2021-08-19 12:32:31.647050+0800 004-NSCondition[61699:2558489] 消费一个 还剩 count 1 
2021-08-19 12:32:31.647431+0800 004-NSCondition[61699:2558418] 消费一个 还剩 count 0 
2021-08-19 12:32:31.647801+0800 004-NSCondition[61699:2558475] 生产一个 现有 count 1
2021-08-19 12:32:31.648134+0800 004-NSCondition[61699:2558477] 生产一个 现有 count 2
2021-08-19 12:32:31.648604+0800 004-NSCondition[61699:2558482] 消费一个 还剩 count 1 
2021-08-19 12:32:31.648885+0800 004-NSCondition[61699:2558476] 消费一个 还剩 count 0 
2021-08-19 12:32:31.649077+0800 004-NSCondition[61699:2558461] 生产一个 现有 count 1
2021-08-19 12:32:31.649520+0800 004-NSCondition[61699:2558491] 生产一个 现有 count 2
2021-08-19 12:32:31.649883+0800 004-NSCondition[61699:2558463] 消费一个 还剩 count 1 
2021-08-19 12:32:31.650396+0800 004-NSCondition[61699:2558420] 消费一个 还剩 count 0 
2021-08-19 12:32:31.650818+0800 004-NSCondition[61699:2558476] 生产一个 现有 count 1
2021-08-19 12:32:31.651033+0800 004-NSCondition[61699:2558481] 生产一个 现有 count 2
2021-08-19 12:32:31.651356+0800 004-NSCondition[61699:2558482] 消费一个 还剩 count 1 
2021-08-19 12:32:31.651692+0800 004-NSCondition[61699:2558477] 消费一个 还剩 count 0 
2021-08-19 12:32:31.652120+0800 004-NSCondition[61699:2558475] 生产一个 现有 count 1
2021-08-19 12:32:31.652372+0800 004-NSCondition[61699:2558420] 生产一个 现有 count 2
2021-08-19 12:32:31.652710+0800 004-NSCondition[61699:2558463] 消费一个 还剩 count 1 
2021-08-19 12:32:31.653357+0800 004-NSCondition[61699:2558491] 生产一个 现有 count 1
2021-08-19 12:32:31.653357+0800 004-NSCondition[61699:2558480] 消费一个 还剩 count 0 
2021-08-19 12:32:31.666438+0800 004-NSCondition[61699:2558432] 消费一个 还剩 count 0 

这里出现消费一个,再消费一个的情况,因为我们生产一个,消费一个,这才是合理的逻辑,这里出现了数据混乱。
我们加上锁,执行,如下:

2021-08-19 12:36:07.780594+0800 004-NSCondition[61773:2562661] 生产一个 现有 count 1
2021-08-19 12:36:07.780827+0800 004-NSCondition[61773:2562661] 生产一个 现有 count 2
2021-08-19 12:36:07.781001+0800 004-NSCondition[61773:2562656] 消费一个 还剩 count 1 
2021-08-19 12:36:07.781145+0800 004-NSCondition[61773:2562656] 消费一个 还剩 count 0 
2021-08-19 12:36:07.781324+0800 004-NSCondition[61773:2562656] 生产一个 现有 count 1
2021-08-19 12:36:07.781605+0800 004-NSCondition[61773:2562661] 消费一个 还剩 count 0 
2021-08-19 12:36:07.781761+0800 004-NSCondition[61773:2562659] 生产一个 现有 count 1
2021-08-19 12:36:07.781901+0800 004-NSCondition[61773:2562658] 消费一个 还剩 count 0 
2021-08-19 12:36:07.782335+0800 004-NSCondition[61773:2562664] 生产一个 现有 count 1
2021-08-19 12:36:07.782840+0800 004-NSCondition[61773:2562657] 消费一个 还剩 count 0 
2021-08-19 12:36:07.783372+0800 004-NSCondition[61773:2562657] 等待 count 0
2021-08-19 12:36:07.783920+0800 004-NSCondition[61773:2562656] 生产一个 现有 count 1
2021-08-19 12:36:07.784388+0800 004-NSCondition[61773:2562657] 消费一个 还剩 count 0 
2021-08-19 12:36:07.784842+0800 004-NSCondition[61773:2562659] 等待 count 0
2021-08-19 12:36:07.785240+0800 004-NSCondition[61773:2562658] 等待 count 0
2021-08-19 12:36:07.785651+0800 004-NSCondition[61773:2562664] 生产一个 现有 count 1
2021-08-19 12:36:07.790800+0800 004-NSCondition[61773:2562719] 生产一个 现有 count 2
2021-08-19 12:36:07.791454+0800 004-NSCondition[61773:2562671] 生产一个 现有 count 3
2021-08-19 12:36:07.792516+0800 004-NSCondition[61773:2562673] 生产一个 现有 count 4
2021-08-19 12:36:07.793068+0800 004-NSCondition[61773:2562672] 消费一个 还剩 count 3 
2021-08-19 12:36:07.793829+0800 004-NSCondition[61773:2562672] 生产一个 现有 count 4
2021-08-19 12:36:07.794326+0800 004-NSCondition[61773:2562656] 生产一个 现有 count 5
2021-08-19 12:36:07.794915+0800 004-NSCondition[61773:2562656] 消费一个 还剩 count 4 
2021-08-19 12:36:07.795709+0800 004-NSCondition[61773:2562656] 生产一个 现有 count 5
2021-08-19 12:36:07.796736+0800 004-NSCondition[61773:2562675] 生产一个 现有 count 6
2021-08-19 12:36:07.797724+0800 004-NSCondition[61773:2562676] 生产一个 现有 count 7
2021-08-19 12:36:07.799552+0800 004-NSCondition[61773:2562676] 生产一个 现有 count 8
2021-08-19 12:36:07.802982+0800 004-NSCondition[61773:2562676] 消费一个 还剩 count 7 
2021-08-19 12:36:07.803195+0800 004-NSCondition[61773:2562676] 消费一个 还剩 count 6 
2021-08-19 12:36:07.803661+0800 004-NSCondition[61773:2562680] 生产一个 现有 count 7
2021-08-19 12:36:07.806095+0800 004-NSCondition[61773:2562681] 消费一个 还剩 count 6 
2021-08-19 12:36:07.806856+0800 004-NSCondition[61773:2562682] 消费一个 还剩 count 5 
2021-08-19 12:36:07.807966+0800 004-NSCondition[61773:2562683] 生产一个 现有 count 6
2021-08-19 12:36:07.808404+0800 004-NSCondition[61773:2562684] 生产一个 现有 count 7
2021-08-19 12:36:07.809210+0800 004-NSCondition[61773:2562684] 消费一个 还剩 count 6 
2021-08-19 12:36:07.809602+0800 004-NSCondition[61773:2562684] 生产一个 现有 count 7
2021-08-19 12:36:07.809905+0800 004-NSCondition[61773:2562684] 生产一个 现有 count 8
2021-08-19 12:36:07.810297+0800 004-NSCondition[61773:2562684] 消费一个 还剩 count 7 
2021-08-19 12:36:07.810953+0800 004-NSCondition[61773:2562684] 消费一个 还剩 count 6 
2021-08-19 12:36:07.811457+0800 004-NSCondition[61773:2562690] 消费一个 还剩 count 5 
2021-08-19 12:36:07.813341+0800 004-NSCondition[61773:2562690] 生产一个 现有 count 6
2021-08-19 12:36:07.821657+0800 004-NSCondition[61773:2562690] 消费一个 还剩 count 5 
2021-08-19 12:36:07.823451+0800 004-NSCondition[61773:2562690] 消费一个 还剩 count 4 
2021-08-19 12:36:07.824456+0800 004-NSCondition[61773:2562690] 生产一个 现有 count 5
2021-08-19 12:36:07.824940+0800 004-NSCondition[61773:2562695] 生产一个 现有 count 6
2021-08-19 12:36:07.825340+0800 004-NSCondition[61773:2562696] 生产一个 现有 count 7
2021-08-19 12:36:07.826224+0800 004-NSCondition[61773:2562696] 消费一个 还剩 count 6 
2021-08-19 12:36:07.827103+0800 004-NSCondition[61773:2562698] 消费一个 还剩 count 5 
2021-08-19 12:36:07.827632+0800 004-NSCondition[61773:2562699] 生产一个 现有 count 6
2021-08-19 12:36:07.828368+0800 004-NSCondition[61773:2562700] 生产一个 现有 count 7
2021-08-19 12:36:07.828643+0800 004-NSCondition[61773:2562701] 消费一个 还剩 count 6 
2021-08-19 12:36:07.829148+0800 004-NSCondition[61773:2562702] 消费一个 还剩 count 5 
2021-08-19 12:36:07.829479+0800 004-NSCondition[61773:2562702] 生产一个 现有 count 6
2021-08-19 12:36:07.829900+0800 004-NSCondition[61773:2562704] 生产一个 现有 count 7
2021-08-19 12:36:07.830165+0800 004-NSCondition[61773:2562704] 消费一个 还剩 count 6 
2021-08-19 12:36:07.831331+0800 004-NSCondition[61773:2562704] 生产一个 现有 count 7
2021-08-19 12:36:07.831630+0800 004-NSCondition[61773:2562707] 生产一个 现有 count 8
2021-08-19 12:36:07.832299+0800 004-NSCondition[61773:2562708] 生产一个 现有 count 9
2021-08-19 12:36:07.832821+0800 004-NSCondition[61773:2562709] 消费一个 还剩 count 8 
2021-08-19 12:36:07.833168+0800 004-NSCondition[61773:2562710] 消费一个 还剩 count 7 
2021-08-19 12:36:07.833564+0800 004-NSCondition[61773:2562711] 生产一个 现有 count 8
2021-08-19 12:36:07.834048+0800 004-NSCondition[61773:2562712] 生产一个 现有 count 9
2021-08-19 12:36:07.835604+0800 004-NSCondition[61773:2562712] 消费一个 还剩 count 8 
2021-08-19 12:36:07.835958+0800 004-NSCondition[61773:2562712] 生产一个 现有 count 9
2021-08-19 12:36:07.837085+0800 004-NSCondition[61773:2562715] 生产一个 现有 count 10
2021-08-19 12:36:07.837716+0800 004-NSCondition[61773:2562715] 消费一个 还剩 count 9 
2021-08-19 12:36:07.838486+0800 004-NSCondition[61773:2562717] 消费一个 还剩 count 8 
2021-08-19 12:36:07.839045+0800 004-NSCondition[61773:2562718] 消费一个 还剩 count 7 
2021-08-19 12:36:07.839495+0800 004-NSCondition[61773:2562664] 生产一个 现有 count 8
2021-08-19 12:36:07.840166+0800 004-NSCondition[61773:2562664] 消费一个 还剩 count 7 
2021-08-19 12:36:07.840493+0800 004-NSCondition[61773:2562670] 消费一个 还剩 count 6 
2021-08-19 12:36:07.840734+0800 004-NSCondition[61773:2562658] 消费一个 还剩 count 5 
2021-08-19 12:36:07.841315+0800 004-NSCondition[61773:2562719] 消费一个 还剩 count 4 
2021-08-19 12:36:07.842119+0800 004-NSCondition[61773:2562719] 消费一个 还剩 count 3 
2021-08-19 12:36:07.842486+0800 004-NSCondition[61773:2562719] 消费一个 还剩 count 2 
2021-08-19 12:36:07.843162+0800 004-NSCondition[61773:2562661] 生产一个 现有 count 3
2021-08-19 12:36:07.843494+0800 004-NSCondition[61773:2562672] 消费一个 还剩 count 2 
2021-08-19 12:36:07.843858+0800 004-NSCondition[61773:2562657] 消费一个 还剩 count 1 
2021-08-19 12:36:07.844352+0800 004-NSCondition[61773:2562657] 消费一个 还剩 count 0 
2021-08-19 12:36:07.845072+0800 004-NSCondition[61773:2562657] 生产一个 现有 count 1
2021-08-19 12:36:07.845303+0800 004-NSCondition[61773:2562721] 消费一个 还剩 count 0 
2021-08-19 12:36:07.846187+0800 004-NSCondition[61773:2562722] 等待 count 0
2021-08-19 12:36:07.847638+0800 004-NSCondition[61773:2562723] 生产一个 现有 count 1
2021-08-19 12:36:07.848177+0800 004-NSCondition[61773:2562722] 消费一个 还剩 count 0 
2021-08-19 12:36:07.848687+0800 004-NSCondition[61773:2562725] 等待 count 0
2021-08-19 12:36:07.849422+0800 004-NSCondition[61773:2562656] 等待 count 0
2021-08-19 12:36:07.850098+0800 004-NSCondition[61773:2562675] 生产一个 现有 count 1
2021-08-19 12:36:07.851221+0800 004-NSCondition[61773:2562725] 消费一个 还剩 count 0 
2021-08-19 12:36:07.851680+0800 004-NSCondition[61773:2562678] 等待 count 0
2021-08-19 12:36:07.853248+0800 004-NSCondition[61773:2562679] 生产一个 现有 count 1
2021-08-19 12:36:07.853869+0800 004-NSCondition[61773:2562676] 生产一个 现有 count 2
2021-08-19 12:36:07.854954+0800 004-NSCondition[61773:2562678] 消费一个 还剩 count 1 
2021-08-19 12:36:07.856389+0800 004-NSCondition[61773:2562681] 消费一个 还剩 count 0 
2021-08-19 12:36:07.856895+0800 004-NSCondition[61773:2562682] 等待 count 0
2021-08-19 12:36:07.857306+0800 004-NSCondition[61773:2562683] 生产一个 现有 count 1
2021-08-19 12:36:07.857750+0800 004-NSCondition[61773:2562682] 消费一个 还剩 count 0 
2021-08-19 12:36:07.860946+0800 004-NSCondition[61773:2562682] 生产一个 现有 count 1
2021-08-19 12:36:07.862151+0800 004-NSCondition[61773:2562729] 消费一个 还剩 count 0 
2021-08-19 12:36:07.862393+0800 004-NSCondition[61773:2562686] 等待 count 0
2021-08-19 12:36:07.862660+0800 004-NSCondition[61773:2562687] 生产一个 现有 count 1
2021-08-19 12:36:07.862958+0800 004-NSCondition[61773:2562688] 生产一个 现有 count 2
2021-08-19 12:36:07.863792+0800 004-NSCondition[61773:2562688] 生产一个 现有 count 3
2021-08-19 12:36:07.869803+0800 004-NSCondition[61773:2562684] 生产一个 现有 count 4
2021-08-19 12:36:07.870514+0800 004-NSCondition[61773:2562684] 消费一个 还剩 count 3 
2021-08-19 12:36:07.873553+0800 004-NSCondition[61773:2562692] 生产一个 现有 count 4
2021-08-19 12:36:07.874406+0800 004-NSCondition[61773:2562693] 消费一个 还剩 count 3 
2021-08-19 12:36:07.875088+0800 004-NSCondition[61773:2562694] 消费一个 还剩 count 2 
2021-08-19 12:36:07.876437+0800 004-NSCondition[61773:2562690] 生产一个 现有 count 3
2021-08-19 12:36:07.876813+0800 004-NSCondition[61773:2562695] 消费一个 还剩 count 2 
2021-08-19 12:36:07.877435+0800 004-NSCondition[61773:2562697] 消费一个 还剩 count 1 
2021-08-19 12:36:07.877936+0800 004-NSCondition[61773:2562696] 生产一个 现有 count 2
2021-08-19 12:36:07.878481+0800 004-NSCondition[61773:2562698] 生产一个 现有 count 3
2021-08-19 12:36:07.878839+0800 004-NSCondition[61773:2562699] 消费一个 还剩 count 2 
2021-08-19 12:36:07.996559+0800 004-NSCondition[61773:2562700] 消费一个 还剩 count 1 
2021-08-19 12:36:07.997821+0800 004-NSCondition[61773:2562701] 生产一个 现有 count 2
2021-08-19 12:36:07.999261+0800 004-NSCondition[61773:2562703] 生产一个 现有 count 3
2021-08-19 12:36:08.000533+0800 004-NSCondition[61773:2562702] 消费一个 还剩 count 2 
2021-08-19 12:36:08.002534+0800 004-NSCondition[61773:2562705] 消费一个 还剩 count 1 
2021-08-19 12:36:08.004080+0800 004-NSCondition[61773:2562705] 生产一个 现有 count 2
2021-08-19 12:36:08.005027+0800 004-NSCondition[61773:2562704] 生产一个 现有 count 3
2021-08-19 12:36:08.005297+0800 004-NSCondition[61773:2562707] 消费一个 还剩 count 2 
2021-08-19 12:36:08.005540+0800 004-NSCondition[61773:2562708] 消费一个 还剩 count 1 
2021-08-19 12:36:08.006059+0800 004-NSCondition[61773:2562709] 生产一个 现有 count 2
2021-08-19 12:36:08.006697+0800 004-NSCondition[61773:2562710] 生产一个 现有 count 3
2021-08-19 12:36:08.007570+0800 004-NSCondition[61773:2562711] 消费一个 还剩 count 2 
2021-08-19 12:36:08.007763+0800 004-NSCondition[61773:2562711] 生产一个 现有 count 3
2021-08-19 12:36:08.007933+0800 004-NSCondition[61773:2562714] 消费一个 还剩 count 2 
2021-08-19 12:36:08.008249+0800 004-NSCondition[61773:2562712] 生产一个 现有 count 3
2021-08-19 12:36:08.032662+0800 004-NSCondition[61773:2562716] 生产一个 现有 count 4
2021-08-19 12:36:08.033593+0800 004-NSCondition[61773:2562715] 消费一个 还剩 count 3 
2021-08-19 12:36:08.034155+0800 004-NSCondition[61773:2562717] 生产一个 现有 count 4
2021-08-19 12:36:08.034951+0800 004-NSCondition[61773:2562718] 生产一个 现有 count 5
2021-08-19 12:36:08.036092+0800 004-NSCondition[61773:2562718] 消费一个 还剩 count 4 
2021-08-19 12:36:08.037605+0800 004-NSCondition[61773:2562664] 消费一个 还剩 count 3 
2021-08-19 12:36:08.038858+0800 004-NSCondition[61773:2562670] 生产一个 现有 count 4
2021-08-19 12:36:08.039862+0800 004-NSCondition[61773:2562658] 生产一个 现有 count 5
2021-08-19 12:36:08.040082+0800 004-NSCondition[61773:2562671] 消费一个 还剩 count 4 
2021-08-19 12:36:08.040234+0800 004-NSCondition[61773:2562671] 生产一个 现有 count 5
2021-08-19 12:36:08.040361+0800 004-NSCondition[61773:2562671] 生产一个 现有 count 6
2021-08-19 12:36:08.055455+0800 004-NSCondition[61773:2562661] 生产一个 现有 count 7
2021-08-19 12:36:08.055733+0800 004-NSCondition[61773:2562661] 消费一个 还剩 count 6 
2021-08-19 12:36:08.055960+0800 004-NSCondition[61773:2562674] 消费一个 还剩 count 5 
2021-08-19 12:36:08.056177+0800 004-NSCondition[61773:2562720] 生产一个 现有 count 6
2021-08-19 12:36:08.056393+0800 004-NSCondition[61773:2562657] 生产一个 现有 count 7
2021-08-19 12:36:08.056605+0800 004-NSCondition[61773:2562721] 消费一个 还剩 count 6 
2021-08-19 12:36:08.056774+0800 004-NSCondition[61773:2562721] 生产一个 现有 count 7
2021-08-19 12:36:08.057592+0800 004-NSCondition[61773:2562724] 生产一个 现有 count 8
2021-08-19 12:36:08.061280+0800 004-NSCondition[61773:2562722] 生产一个 现有 count 9
2021-08-19 12:36:08.070331+0800 004-NSCondition[61773:2562675] 生产一个 现有 count 10
2021-08-19 12:36:08.072080+0800 004-NSCondition[61773:2562677] 消费一个 还剩 count 9 
2021-08-19 12:36:08.073954+0800 004-NSCondition[61773:2562725] 消费一个 还剩 count 8 
2021-08-19 12:36:08.074166+0800 004-NSCondition[61773:2562679] 消费一个 还剩 count 7 
2021-08-19 12:36:08.074341+0800 004-NSCondition[61773:2562656] 消费一个 还剩 count 6 
2021-08-19 12:36:08.074597+0800 004-NSCondition[61773:2562680] 生产一个 现有 count 7
2021-08-19 12:36:08.075125+0800 004-NSCondition[61773:2562676] 生产一个 现有 count 8
2021-08-19 12:36:08.075694+0800 004-NSCondition[61773:2562678] 生产一个 现有 count 9
2021-08-19 12:36:08.078484+0800 004-NSCondition[61773:2562681] 消费一个 还剩 count 8 
2021-08-19 12:36:08.088618+0800 004-NSCondition[61773:2562728] 生产一个 现有 count 9
2021-08-19 12:36:08.091309+0800 004-NSCondition[61773:2562683] 消费一个 还剩 count 8 
2021-08-19 12:36:08.091871+0800 004-NSCondition[61773:2562685] 消费一个 还剩 count 7 
2021-08-19 12:36:08.094234+0800 004-NSCondition[61773:2562682] 生产一个 现有 count 8
2021-08-19 12:36:08.100436+0800 004-NSCondition[61773:2562729] 消费一个 还剩 count 7 
2021-08-19 12:36:08.104815+0800 004-NSCondition[61773:2562686] 消费一个 还剩 count 6 
2021-08-19 12:36:08.104990+0800 004-NSCondition[61773:2562687] 消费一个 还剩 count 5 
2021-08-19 12:36:08.105155+0800 004-NSCondition[61773:2562689] 消费一个 还剩 count 4 
2021-08-19 12:36:08.105328+0800 004-NSCondition[61773:2562688] 生产一个 现有 count 5
2021-08-19 12:36:08.105493+0800 004-NSCondition[61773:2562691] 生产一个 现有 count 6
2021-08-19 12:36:08.105647+0800 004-NSCondition[61773:2562684] 消费一个 还剩 count 5 
2021-08-19 12:36:08.105779+0800 004-NSCondition[61773:2562692] 生产一个 现有 count 6
2021-08-19 12:36:08.106091+0800 004-NSCondition[61773:2562693] 生产一个 现有 count 7
2021-08-19 12:36:08.106554+0800 004-NSCondition[61773:2562694] 消费一个 还剩 count 6 
2021-08-19 12:36:08.107011+0800 004-NSCondition[61773:2562690] 消费一个 还剩 count 5 
2021-08-19 12:36:08.107548+0800 004-NSCondition[61773:2562695] 生产一个 现有 count 6
2021-08-19 12:36:08.119057+0800 004-NSCondition[61773:2562697] 生产一个 现有 count 7
2021-08-19 12:36:08.119259+0800 004-NSCondition[61773:2562696] 消费一个 还剩 count 6 
2021-08-19 12:36:08.119427+0800 004-NSCondition[61773:2562698] 消费一个 还剩 count 5 
2021-08-19 12:36:08.119581+0800 004-NSCondition[61773:2562699] 生产一个 现有 count 6
2021-08-19 12:36:08.119880+0800 004-NSCondition[61773:2562700] 生产一个 现有 count 7
2021-08-19 12:36:08.120389+0800 004-NSCondition[61773:2562701] 消费一个 还剩 count 6 
2021-08-19 12:36:08.120859+0800 004-NSCondition[61773:2562703] 消费一个 还剩 count 5 
2021-08-19 12:36:08.121286+0800 004-NSCondition[61773:2562702] 生产一个 现有 count 6
2021-08-19 12:36:08.121767+0800 004-NSCondition[61773:2562706] 消费一个 还剩 count 5 
2021-08-19 12:36:08.122098+0800 004-NSCondition[61773:2562705] 消费一个 还剩 count 4 
2021-08-19 12:36:08.122469+0800 004-NSCondition[61773:2562704] 消费一个 还剩 count 3 
2021-08-19 12:36:08.122896+0800 004-NSCondition[61773:2562707] 生产一个 现有 count 4
2021-08-19 12:36:08.123505+0800 004-NSCondition[61773:2562708] 生产一个 现有 count 5
2021-08-19 12:36:08.125252+0800 004-NSCondition[61773:2562709] 消费一个 还剩 count 4 
2021-08-19 12:36:08.128056+0800 004-NSCondition[61773:2562710] 消费一个 还剩 count 3 
2021-08-19 12:36:08.130918+0800 004-NSCondition[61773:2562713] 消费一个 还剩 count 2 
2021-08-19 12:36:08.135223+0800 004-NSCondition[61773:2562711] 生产一个 现有 count 3
2021-08-19 12:36:08.136428+0800 004-NSCondition[61773:2562714] 消费一个 还剩 count 2 
2021-08-19 12:36:08.136892+0800 004-NSCondition[61773:2562712] 消费一个 还剩 count 1 
2021-08-19 12:36:08.137286+0800 004-NSCondition[61773:2562716] 生产一个 现有 count 2
2021-08-19 12:36:08.137652+0800 004-NSCondition[61773:2562715] 生产一个 现有 count 3
2021-08-19 12:36:08.138076+0800 004-NSCondition[61773:2562717] 消费一个 还剩 count 2 
2021-08-19 12:36:08.138416+0800 004-NSCondition[61773:2562659] 消费一个 还剩 count 1 
2021-08-19 12:36:08.139008+0800 004-NSCondition[61773:2562718] 生产一个 现有 count 2
2021-08-19 12:36:08.139530+0800 004-NSCondition[61773:2562664] 生产一个 现有 count 3
2021-08-19 12:36:08.139942+0800 004-NSCondition[61773:2562670] 消费一个 还剩 count 2 
2021-08-19 12:36:08.140481+0800 004-NSCondition[61773:2562658] 消费一个 还剩 count 1 
2021-08-19 12:36:08.141183+0800 004-NSCondition[61773:2562673] 生产一个 现有 count 2
2021-08-19 12:36:08.144723+0800 004-NSCondition[61773:2562719] 生产一个 现有 count 3
2021-08-19 12:36:08.148864+0800 004-NSCondition[61773:2562671] 消费一个 还剩 count 2 
2021-08-19 12:36:08.151855+0800 004-NSCondition[61773:2562672] 消费一个 还剩 count 1 
2021-08-19 12:36:08.153266+0800 004-NSCondition[61773:2562661] 生产一个 现有 count 2
2021-08-19 12:36:08.153638+0800 004-NSCondition[61773:2562674] 生产一个 现有 count 3
2021-08-19 12:36:08.154041+0800 004-NSCondition[61773:2562720] 消费一个 还剩 count 2 
2021-08-19 12:36:08.154577+0800 004-NSCondition[61773:2562657] 消费一个 还剩 count 1 
2021-08-19 12:36:08.155147+0800 004-NSCondition[61773:2562723] 消费一个 还剩 count 0 

这里就没有出现过度消费的情况,生产一个,消费一个,如self.ticketCount==0,就出现信号等待。
生产进行加锁,消费加锁保证了事务的安全。

4 Foundation源码关于锁的封装

因为NSLock在Foundation框架中,没有开源 我们要分析NSLock的底层,怎么办呢,
我们可以通过swift的foundation开源代码来分析。
我们能一系列的分析NLock的lock是NSLocking的协议方法,只要实现NSLocking协议,就会有lockunlock两个方法,这个时候结合swift的Foundation源码看,找到如下代码。

public protocol NSLocking {
    func lock()
    func unlock()
}

我们再看下它的实现

open class NSLock: NSObject, NSLocking {
    internal var mutex = _MutexPointer.allocate(capacity: 1)
#if os(macOS) || os(iOS) || os(Windows)
    private var timeoutCond = _ConditionVariablePointer.allocate(capacity: 1)
    private var timeoutMutex = _MutexPointer.allocate(capacity: 1)
#endif

    public override init() {
#if os(Windows)
        InitializeSRWLock(mutex)
        InitializeConditionVariable(timeoutCond)
        InitializeSRWLock(timeoutMutex)
#else
        pthread_mutex_init(mutex, nil)
#if os(macOS) || os(iOS)
        pthread_cond_init(timeoutCond, nil)
        pthread_mutex_init(timeoutMutex, nil)
#endif
#endif
    }
    
    deinit {
#if os(Windows)
        // SRWLocks do not need to be explicitly destroyed
#else
        pthread_mutex_destroy(mutex)
#endif
        mutex.deinitialize(count: 1)
        mutex.deallocate()
#if os(macOS) || os(iOS) || os(Windows)
        deallocateTimedLockData(cond: timeoutCond, mutex: timeoutMutex)
#endif
    }
    
    open func lock() {
#if os(Windows)
        AcquireSRWLockExclusive(mutex)
#else
        pthread_mutex_lock(mutex)
#endif
    }

    open func unlock() {
#if os(Windows)
        ReleaseSRWLockExclusive(mutex)
        AcquireSRWLockExclusive(timeoutMutex)
        WakeAllConditionVariable(timeoutCond)
        ReleaseSRWLockExclusive(timeoutMutex)
#else
        pthread_mutex_unlock(mutex)
#if os(macOS) || os(iOS)
        // Wakeup any threads waiting in lock(before:)
        pthread_mutex_lock(timeoutMutex)
        pthread_cond_broadcast(timeoutCond)
        pthread_mutex_unlock(timeoutMutex)
#endif
#endif
    }

    open func `try`() -> Bool {
#if os(Windows)
        return TryAcquireSRWLockExclusive(mutex) != 0
#else
        return pthread_mutex_trylock(mutex) == 0
#endif
    }
    
    open func lock(before limit: Date) -> Bool {
#if os(Windows)
        if TryAcquireSRWLockExclusive(mutex) != 0 {
          return true
        }
#else
        if pthread_mutex_trylock(mutex) == 0 {
            return true
        }
#endif

#if os(macOS) || os(iOS) || os(Windows)
        return timedLock(mutex: mutex, endTime: limit, using: timeoutCond, with: timeoutMutex)
#else
        guard var endTime = timeSpecFrom(date: limit) else {
            return false
        }
        return pthread_mutex_timedlock(mutex, &endTime) == 0
#endif
    }

    open var name: String?
}

找到这段代码,其中这里这段代码:

#if os(Windows)
        InitializeSRWLock(mutex)
        InitializeConditionVariable(timeoutCond)
        InitializeSRWLock(timeoutMutex)
#else
        pthread_mutex_init(mutex, nil)
#if os(macOS) || os(iOS)
        pthread_cond_init(timeoutCond, nil)
        pthread_mutex_init(timeoutMutex, nil)
#endif
#endif

底层使用pthread封装 .
我们再来看下NSRecursiveLock锁的原理,通过搜索,找到以下代码:

open class NSRecursiveLock: NSObject, NSLocking {
    internal var mutex = _RecursiveMutexPointer.allocate(capacity: 1)
#if os(macOS) || os(iOS) || os(Windows)
    private var timeoutCond = _ConditionVariablePointer.allocate(capacity: 1)
    private var timeoutMutex = _MutexPointer.allocate(capacity: 1)
#endif

    public override init() {
        super.init()
#if os(Windows)
        InitializeCriticalSection(mutex)
        InitializeConditionVariable(timeoutCond)
        InitializeSRWLock(timeoutMutex)
#else
#if CYGWIN
        var attrib : pthread_mutexattr_t? = nil
#else
        var attrib = pthread_mutexattr_t()
#endif
        withUnsafeMutablePointer(to: &attrib) { attrs in
            pthread_mutexattr_init(attrs)
            pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))
            pthread_mutex_init(mutex, attrs)
        }
#if os(macOS) || os(iOS)
        pthread_cond_init(timeoutCond, nil)
        pthread_mutex_init(timeoutMutex, nil)
#endif
#endif
    }
    
    deinit {
#if os(Windows)
        DeleteCriticalSection(mutex)
#else
        pthread_mutex_destroy(mutex)
#endif
        mutex.deinitialize(count: 1)
        mutex.deallocate()
#if os(macOS) || os(iOS) || os(Windows)
        deallocateTimedLockData(cond: timeoutCond, mutex: timeoutMutex)
#endif
    }
    
    open func lock() {
#if os(Windows)
        EnterCriticalSection(mutex)
#else
        pthread_mutex_lock(mutex)
#endif
    }
    
    open func unlock() {
#if os(Windows)
        LeaveCriticalSection(mutex)
        AcquireSRWLockExclusive(timeoutMutex)
        WakeAllConditionVariable(timeoutCond)
        ReleaseSRWLockExclusive(timeoutMutex)
#else
        pthread_mutex_unlock(mutex)
#if os(macOS) || os(iOS)
        // Wakeup any threads waiting in lock(before:)
        pthread_mutex_lock(timeoutMutex)
        pthread_cond_broadcast(timeoutCond)
        pthread_mutex_unlock(timeoutMutex)
#endif
#endif
    }
    
    open func `try`() -> Bool {
#if os(Windows)
        return TryEnterCriticalSection(mutex)
#else
        return pthread_mutex_trylock(mutex) == 0
#endif
    }
    
    open func lock(before limit: Date) -> Bool {
#if os(Windows)
        if TryEnterCriticalSection(mutex) {
            return true
        }
#else
        if pthread_mutex_trylock(mutex) == 0 {
            return true
        }
#endif

#if os(macOS) || os(iOS) || os(Windows)
        return timedLock(mutex: mutex, endTime: limit, using: timeoutCond, with: timeoutMutex)
#else
        guard var endTime = timeSpecFrom(date: limit) else {
            return false
        }
        return pthread_mutex_timedlock(mutex, &endTime) == 0
#endif
    }

    open var name: String?
}

它的源码中,有这样一段

#if os(macOS) || os(iOS)
        pthread_cond_init(timeoutCond, nil)
        pthread_mutex_init(timeoutMutex, nil)
#endif
#endif

不难看出也是在底层封装了pthread。
NSLockNSRecursiveLock底层都是用pthread封装的,为什么NSLock不是递归,NSRecursiveLock是递归呢,我们来看下。
NSRecursiveLock

#if CYGWIN
        var attrib : pthread_mutexattr_t? = nil
#else
        var attrib = pthread_mutexattr_t()
#endif
        withUnsafeMutablePointer(to: &attrib) { attrs in
            pthread_mutexattr_init(attrs)
            pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))
            pthread_mutex_init(mutex, attrs)

 pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))

有这些代码,而NSLock没有这些东西。
我们再来看NSCondition,找到如下代码:

open class NSCondition: NSObject, NSLocking {
    internal var mutex = _MutexPointer.allocate(capacity: 1)
    internal var cond = _ConditionVariablePointer.allocate(capacity: 1)

    public override init() {
#if os(Windows)
        InitializeSRWLock(mutex)
        InitializeConditionVariable(cond)
#else
        pthread_mutex_init(mutex, nil)
        pthread_cond_init(cond, nil)
#endif
    }
    
    deinit {
#if os(Windows)
        // SRWLock do not need to be explicitly destroyed
#else
        pthread_mutex_destroy(mutex)
        pthread_cond_destroy(cond)
#endif
        mutex.deinitialize(count: 1)
        cond.deinitialize(count: 1)
        mutex.deallocate()
        cond.deallocate()
    }
    
    open func lock() {
#if os(Windows)
        AcquireSRWLockExclusive(mutex)
#else
        pthread_mutex_lock(mutex)
#endif
    }
    
    open func unlock() {
#if os(Windows)
        ReleaseSRWLockExclusive(mutex)
#else
        pthread_mutex_unlock(mutex)
#endif
    }
    
    open func wait() {
#if os(Windows)
        SleepConditionVariableSRW(cond, mutex, WinSDK.INFINITE, 0)
#else
        pthread_cond_wait(cond, mutex)
#endif
    }

    open func wait(until limit: Date) -> Bool {
#if os(Windows)
        return SleepConditionVariableSRW(cond, mutex, timeoutFrom(date: limit), 0)
#else
        guard var timeout = timeSpecFrom(date: limit) else {
            return false
        }
        return pthread_cond_timedwait(cond, mutex, &timeout) == 0
#endif
    }
    
    open func signal() {
#if os(Windows)
        WakeConditionVariable(cond)
#else
        pthread_cond_signal(cond)
#endif
    }
    
    open func broadcast() {
#if os(Windows)
        WakeAllConditionVariable(cond)
#else
        // 汇编分析 - 猜 (多看多玩)
        pthread_cond_broadcast(cond) // wait  signal
#endif
    }
    
    open var name: String?
}

#if os(Windows)
private func timeoutFrom(date: Date) -> DWORD {
  guard date.timeIntervalSinceNow > 0 else { return 0 }
  return DWORD(date.timeIntervalSinceNow * 1000)
}
#else
private func timeSpecFrom(date: Date) -> timespec? {
    guard date.timeIntervalSinceNow > 0 else {
        return nil
    }
    let nsecPerSec: Int64 = 1_000_000_000
    let interval = date.timeIntervalSince1970
    let intervalNS = Int64(interval * Double(nsecPerSec))

    return timespec(tv_sec: Int(intervalNS / nsecPerSec),
                    tv_nsec: Int(intervalNS % nsecPerSec))
}

这里同样也实现了NSLocking协议,同样也是在底层封装了pthread
如果在没有源码的情况下我们该如何分析呢,我们就由NSConditionLock来举例,看看如何分析。

5 NSConditionLock分析

- (void)ro_testConditonLock{
    NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:2];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        [conditionLock lockWhenCondition:1];
        NSLog(@"线程 1");
        [conditionLock unlockWithCondition:0];
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        [conditionLock lockWhenCondition:2];
        sleep(0.1);
        NSLog(@"线程 2");
        [conditionLock unlockWithCondition:1];
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
       [conditionLock lock];
       NSLog(@"线程 3");
       [conditionLock unlock];
    });
}

这是分析的代码,这里的执行顺序是什么?
我们打印下,如图:


3

顺序是3,2,1。这里2和3是无序的,1一定是2之后执行,为什么呢, [[NSConditionLock alloc] initWithCondition:2];*这里initWithCondition等于2,也就是说条件是2 ,如果不等于2是不会执行的。
分析如下:

  • 先进入线程1,发现条件不满足,就等待
  • 这个时候进入线程2 ,发现条件满足,执行
  • 线程执行后,条件变成1,线程1会执行
  • 线程3是因为没有加条件,所以就先执行

我们发现 NSConditionLockNSCondition有点像

  • 他们有没有关系,我们接下来调试分析下。
  • lockWhenCondition是如何控制的
  • lockWhenCondition中的2是什么
  • unlockWithCondition做了什么
    针对这些疑问,我们断点调试下,还有一个-[NSConditionLock initWithCondition:]符号断点,如图:
    4

    在Foundation中-[NSConditionLock initWithCondition:]这个方法断住了,
    这时候我们读取x0,x1寄存看下,如图
    5

    这里打印结果对应上了。
    在这里我们可以通b跳转指令来查找流程,我们在bl指令的地方打上断点,调试,结果如下:
  • [NSConditionLock initWithCondition:]:
  • [init: 2]
  • -[NSConditionLock init]
  • [NSConditionLock zone]
  • [NSCondition allocWithZone]
  • [NSCondition init]

从这里可以看出NSConditionLock封装了NSCondition,里面存储了value值。
lockWhenConditionunlockWithCondition的流程是怎么样,我们再分析下,通过-[NSConditionLock lockWhenCondition:] 符号断点分析,如图

6

分析结果如下,lockWhenCondition流程:

  • -[NSConditionLock lockWhenCondition:]
  • [NSDate distantFuture]
  • [NSConditionLock lockWhenCondition:(条件) beforeDate:]
    • [NSCondition lock]
    • [NSCondition waitUntilDate:]
    • 返回1 不再等待,可以往下执行
    • [NSCondition unlock]

unlockWithCondition流程:

  • -[NSConditionLock unlockWithCondition:]
  • [NSCondition lock]
  • [NSCondition broadcast]
  • [NSCondition unlock]

我们再对比一下源码

open class NSConditionLock : NSObject, NSLocking {
    internal var _cond = NSCondition()
    internal var _value: Int
    internal var _thread: _swift_CFThreadRef?
    
    public convenience override init() {
        self.init(condition: 0)
    }
    
    public init(condition: Int) {
        _value = condition
    }

    open func lock() {
        let _ = lock(before: Date.distantFuture)
    }

    open func unlock() {
        _cond.lock()
#if os(Windows)
        _thread = INVALID_HANDLE_VALUE
#else
        _thread = nil
#endif
        _cond.broadcast()
        _cond.unlock()
    }
    
    open var condition: Int {
        return _value
    }

    open func lock(whenCondition condition: Int) {
        let _ = lock(whenCondition: condition, before: Date.distantFuture)
    }

    open func `try`() -> Bool {
        return lock(before: Date.distantPast)
    }
    
    open func tryLock(whenCondition condition: Int) -> Bool {
        return lock(whenCondition: condition, before: Date.distantPast)
    }

    open func unlock(withCondition condition: Int) {
        _cond.lock()
#if os(Windows)
        _thread = INVALID_HANDLE_VALUE
#else
        _thread = nil
#endif
        _value = condition
        _cond.broadcast()
        _cond.unlock()
    }

    open func lock(before limit: Date) -> Bool {
        _cond.lock()
        while _thread != nil {
            if !_cond.wait(until: limit) {
                _cond.unlock()
                return false
            }
        }
#if os(Windows)
        _thread = GetCurrentThread()
#else
        _thread = pthread_self()
#endif
        _cond.unlock()
        return true
    }
    
    open func lock(whenCondition condition: Int, before limit: Date) -> Bool {
        _cond.lock()
        while _thread != nil || _value != condition {
            if !_cond.wait(until: limit) {
                _cond.unlock()
                return false
            }
        }
#if os(Windows)
        _thread = GetCurrentThread()
#else
        _thread = pthread_self()
#endif
        _cond.unlock()
        return true
    }
    
    open var name: String?
}

从以上代码可以看出,跟我们上面的得到的流程是一样的。

6 读写锁的简介

读写锁实际是⼀种特殊的互斥锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进⾏读访问,写者则需要对共享资源进⾏写操作。这种锁相对于⾃旋锁⽽⾔,能提⾼并发性,因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最⼤可能的读者数为实际的逻辑CPU数。

写者是排他性的,⼀个读写锁同时只能有⼀个写者或多个读者(与CPU数相关),但不能同时既有读者⼜有写者。在读写锁保持期间也是抢占失效的。如果读写锁当前没有读者,也没有写者,那么写者可以⽴刻获得读写锁,否则它必须⾃旋在那⾥,直到没有任何写者或读者。如果读写锁没有写者,那么读者可以⽴即获得该读写锁,否则读者必须⾃旋在那⾥,直到写者释放该读写锁。⼀次只有⼀个线程可以占有写模式的读写锁, 但是可以有多个线程同时占有读模式的读写锁.

正是因为这个特性,当读写锁是写加锁状态时, 在这个锁被解锁之前, 所有试图对这个锁加锁的线程都会被阻塞.当读写锁在读加锁状态时, 所有试图以读模式对它进⾏加锁的线程都可以得到访问权, 但是如果线程希望以写模式对此锁进⾏加锁, 它必须直到所有的线程释放锁.

通常, 当读写锁处于读模式锁住状态时, 如果有另外线程试图以写模式加锁, 读写锁通常会阻塞随后的读模式锁请求, 这样可以避免读模式锁⻓期占⽤, ⽽等待的写模式锁请求⻓期阻塞.

读写锁适合于对数据结构的读次数⽐写次数多得多的情况. 因为, 读模式锁定时可以共享, 以写模式锁住时意味着独占, 所以读写锁⼜叫共享-独占锁。

7 GCD实现读写锁

对于读写锁,我们需要实现以下几点:

  • 多读单写,利用多条线程读出数据,单写:同一时刻操作只有一个线程操作这片内存空间
  • 写写互斥
  • 读写互斥
  • 读写操作不能影响当前线程的执行
    单写: 我们可以利用GCD的栅栏函数来实现,栅栏函数使我们在写操作的时候,前面读或写操作不能执行,保证写写互斥读写互斥,因为我们要保证读写操作不能影响当前线程的执行, 所以我们就可以用dispatch_barrier_async这个函数来实现,不影响主业务执行。
    多读:dispatch_sync并发队列来实现。
    GCD多读单写代码如下:
    .h文件
#import 

NS_ASSUME_NONNULL_BEGIN

@interface ReadWriteLock : NSObject

- (id)readDataWithKey:(NSString *)key;


- (void)writeDataWithKey:(NSString *)key value:(id)value;
@end

NS_ASSUME_NONNULL_END

.m文件

#import "ReadWriteLock.h"

@interface ReadWriteLock()

@property (strong, nonatomic) dispatch_queue_t queue;

@property (strong, nonatomic) NSMutableDictionary *dict;

@end

@implementation ReadWriteLock

- (id)init {
    self = [super init];
    if (self) {
    }
    return self;
}


- (id)readDataWithKey:(NSString *)key {
    __block id value = nil;
    dispatch_sync(self.queue, ^{
        value = self.dict[key];
    });
    return value;
}


- (void)writeDataWithKey:(NSString *)key value:(id)value {
    dispatch_barrier_async(self.queue, ^{
        NSLog(@"写入:%@ ------- %@", value, [NSThread currentThread]);
        [self.dict setValue:value forKey:key];
    });
}


#pragma GET SET
- (dispatch_queue_t )queue {
    if (!_queue) {
       
        _queue = dispatch_queue_create("ro_robert_queue",DISPATCH_QUEUE_CONCURRENT);
    }
    return _queue;
}

- (NSMutableDictionary *)dict {
    if (!_dict) {
        
        _dict = [[NSMutableDictionary alloc] init];
    }
    return _dict;
}

@end

如果对以上代码有疑问,随时欢迎来交流

结语

这篇文章,我们分析了NSLockNSConditionNSConditionLockNSRecursiveLock这几种锁的原理和流程,如有错误或者遗漏,请大家批评指正。

你可能感兴趣的:(iOS-锁的原理分析(二))