美文网首页
iOS-锁的原理分析(二)

iOS-锁的原理分析(二)

作者: 似水流年_9ebe | 来源:发表于2021-08-19 17:26 被阅读0次

    前言

    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 <Foundation/Foundation.h>
    
    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 ()<NSLocking>
    @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 <Foundation/Foundation.h>
    
    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-锁的原理分析(二)

          本文链接:https://www.haomeiwen.com/subject/zldobltx.html