zoukankan      html  css  js  c++  java
  • 多线程(三) iOS中的锁

    锁的类别:互斥锁,递归锁,条件锁,自旋锁等

    锁的实现方式:NSLock,NSRecursiveLock, NSConditionLock,@synchronized,GCD的信号量等

    下面说一下常用的几种锁:

    1.@synchronized:对象级别所,互斥锁,性能较差不推荐使用

    @synchronized(这里添加一个OC对象,一般使用self) {

            这里写要加锁的代码

        }

      @synchronized使用注意点

      1.加锁的代码尽量少

      2.添加的OC对象必须在多个线程中都是同一对象,下面举一个反例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    - (void)viewDidLoad {
        [super viewDidLoad];
        //设置票的数量为5
        _tickets = 5;
        //线程一
        NSThread *threadOne = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];
        threadOne.name = @"threadOne";
        //线程二
        NSThread *threadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];
         
        //开启线程
        [threadOne start];
        [threadTwo start];
    }
    - (void)saleTickets
    {
        NSObject *object = [[NSObject alloc] init];
        while (1)
        {
            @synchronized(object) {
                [NSThread sleepForTimeInterval:1];
                if (_tickets > 0)
                {
                    _tickets--;
                    NSLog(@"剩余票数= %ld",_tickets);
                }
                else
                {
                    NSLog(@"票卖完了");
                    break;
                }
            }
        }   
    }

     结果卖票又出错了,出现这个原因的问题是每个线程都会创建一个object对象,锁后面加的object在不同线程中就不同了;

    把@synchronized(object)改成 @synchronized(self)就能得到了正确结果

    2.NSLock:互斥锁,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    @interface ViewController ()
    {
        NSLock *mutexLock;
    }
     
    @property (assign, nonatomic)NSInteger tickets;
    @end
     
    @implementation ViewController
     
    - (void)viewDidLoad {
        [super viewDidLoad];
        //创建锁
        mutexLock = [[NSLock alloc] init];
         
        //设置票的数量为5
        _tickets = 5;
        //线程一
        NSThread *threadOne = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];
        threadOne.name = @"threadOne";
        //线程二
        NSThread *threadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];
         
        //开启线程
        [threadOne start];
        [threadTwo start]; 
    }
    - (void)saleTickets
    {
        while (1)
        {
            [NSThread sleepForTimeInterval:1];
            //加锁
            [mutexLock lock];
            if (_tickets > 0)
            {
                _tickets--;
                NSLog(@"剩余票数= %ld",_tickets);
            }
            else
            {
                NSLog(@"票卖完了");
                break;
            }
            //解锁
            [mutexLock unlock];   
        }
    }

     NSLock: 使用注意,不能多次调用 lock方法,会造成死锁

    3.NSRecursiveLock:递归锁

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    @interface ViewController ()
    {
        NSRecursiveLock *rsLock;
    }
     
    @property (assign, nonatomic)NSInteger tickets;
     
     
    @end
     
    @implementation ViewController
     
    - (void)viewDidLoad {
        [super viewDidLoad];
        //创建锁递归锁
        rsLock = [[NSRecursiveLock alloc] init];   
        //设置票的数量为5
        _tickets = 5;
        //线程一
        NSThread *threadOne = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];
        threadOne.name = @"threadOne";
        //线程二
        NSThread *threadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];
        //开启线程
        [threadOne start];
        [threadTwo start];   
    }
    - (void)saleTickets
    {
        while (1)
        {
            [NSThread sleepForTimeInterval:1];
            //加锁,递归锁可以多次加锁
            [rsLock lock];
            [rsLock lock];
            if (_tickets > 0)
            {
                _tickets--;
                NSLog(@"剩余票数= %ld",_tickets);
            }
            else
            {
                NSLog(@"票卖完了");
                break;
            }
            //解锁,只有对应次数解锁,其他线程才能访问。
            [rsLock unlock];
            [rsLock unlock];  
        }   
    }

    4.NSConditionLock:条件锁

    NSConditionLock:条件锁,一个线程获得了锁,其它线程等待。

     [xxxx lock]; 表示 xxx 期待获得锁,如果没有其他线程获得锁(不需要判断内部的condition) 那它能执行此行以下代码,如果已经有其他线程获得锁(可能是条件锁,或者无条件锁),则等待,直至其他线程解锁

     [xxx lockWhenCondition:A条件]; 表示如果没有其他线程获得该锁,但是该锁内部的condition不等于A条件,它依然不能获得锁,仍然等待。如果内部的condition等于A条件,并且没有其他线程获得该锁,则进入代码区,同时设置它获得该锁,其他任何线程都将等待它代码的完成,直至它解锁。

     [xxx unlockWithCondition:A条件]; 表示释放锁,同时把内部的condition设置为A条件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    @interface ViewController () {
        
        NSConditionLock *_cdtLock; //条件锁
     
    }
     
    @end
     
    @implementation ViewController
     
    - (void)viewDidLoad {
        [super viewDidLoad];
         
        //创建条件锁
        _cdtLock = [[NSConditionLock alloc] init];
        [NSThread detachNewThreadSelector:@selector(conditionLockAction1) toTarget:self withObject:nil];
        [NSThread detachNewThreadSelector:@selector(conditionLockAction2) toTarget:self withObject:nil];
         
    }
     
    - (void)conditionLockAction1 {
         
        //阻塞线程2s
        [NSThread sleepForTimeInterval:2];
         
        for (NSInteger i = 0; i < 3; i++) {
            
            //加锁
            [_cdtLock lock];
             
            NSLog(@"i = %li", i);
             
            //释放锁,并设置condition属性的值为i
            [_cdtLock unlockWithCondition:i];
             
        }
    }
     
    - (void)conditionLockAction2 {
         
        //当标识为2时同步代码段才能够执行,如果标识为其它数字则当前线程被阻塞。
        [_cdtLock lockWhenCondition:2];
         
        NSLog(@"thread2");
         
        [_cdtLock unlock];
         
    }

     打印结果:

    如果我们把代码中[_cdtLock lockWhenCondition:2]换成[_cdtLock lockWhenCondition:1]则会发现出现如下结果

    和我们预想的在i = 1后面打印thread2不符合,这是因为conditionLockAction1中的代码段也需要获得锁,同时在循环执行过后把condition置成了2,那么conditionLockAction2就再也没机会加锁了,所以不打印thread2。

    我们可以靠下面的代码验证

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    @interface ViewController () {
        
        NSConditionLock *_cdtLock; //条件锁
     
    }
     
    @end
     
    @implementation ViewController
     
    - (void)viewDidLoad {
        [super viewDidLoad];
         
        //创建条件锁
        _cdtLock = [[NSConditionLock alloc] init];
        [NSThread detachNewThreadSelector:@selector(conditionLockAction1) toTarget:self withObject:nil];
        [NSThread detachNewThreadSelector:@selector(conditionLockAction2) toTarget:self withObject:nil];
         
    }
     
    - (void)conditionLockAction1 {
         
        //阻塞线程2s
        [NSThread sleepForTimeInterval:2];
         
        for (NSInteger i = 0; i < 3; i++) {
            
            //加锁
            [_cdtLock lock];
             
            NSLog(@"i = %li", i);
             
            //释放锁,并设置condition属性的值为i
            [_cdtLock unlockWithCondition:i];
            <span style="color: #ff0000;">//在i 为 1的时候阻塞线程1s
            if (i == 1)
            {
                [NSThread sleepForTimeInterval:1];
            }</span>
             
        }
    }
     
    - (void)conditionLockAction2 {
         
        //当标识为2时同步代码段才能够执行,如果标识为其它数字则当前线程被阻塞。
        [_cdtLock lockWhenCondition:1];
         
        NSLog(@"thread2");
         
        [_cdtLock unlock];
         
    }

     现在的结果就和我们预期的一样了

    5.NSCondition:可以理解为互斥锁和条件锁的结合

     用生产者消费者中的例子可以很好的理解NSCondition

    1.生产者要取得锁,然后去生产,生产后将生产的商品放入库房,如果库房满了,则wait,就释放锁,直到其它线程唤醒它去生产,如果没有满,则生产商品后调用signal,可以唤醒在此condition上等待的线程。

     2.消费者要取得锁,然后去消费,如果当前没有商品,则wait,释放锁,直到有线程去唤醒它消费,如果有商品,则消费后会通知正在等待的生产者去生产商品。

     生产者和消费者的关键是:当库房已满时,生产者等待,不再继续生产商品,当库房已空时,消费者等待,不再继续消费商品,走到库房有商品时,会由生产者通知消费来消费。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    - (void)conditionTest
    {
        //创建数组存放商品
        products = [[NSMutableArray alloc] init];
        condition = [[NSCondition alloc] init];
         
        [NSThread detachNewThreadSelector:@selector(createProducter) toTarget:self withObject:nil];
        [NSThread detachNewThreadSelector:@selector(createConsumenr) toTarget:self withObject:nil];
    }
     
    - (void)createConsumenr
    {
        while (1) {
            //模拟消费商品时间,让它比生产慢一点
            [NSThread sleepForTimeInterval:arc4random()%10 * 0.1 + 1.5];
            [condition lock];
            while (products.count == 0) {
                NSLog(@"商品为0,等待生产");
                [condition wait];
            }
            [products removeLastObject];
            NSLog(@"消费了一个商品,商品数 = %ld",products.count);
            [condition signal];
            [condition unlock];
        }
         
    }
     
    - (void)createProducter
    {
        while (1) {
            //模拟生产商品时间
            [NSThread sleepForTimeInterval:arc4random()%10 * 0.1 + 0.5];
            [condition lock];
            while (products.count == 5)
            {
                NSLog(@"商品满了,等待消费");
                [condition wait];
            }
            [products addObject:[[NSObject alloc] init]];
            NSLog(@"生产了一个商品,商品数%ld",products.count);
            [condition signal];
            [condition unlock];
        }
         
    }

    了解死锁

      概念:死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。

    产生死锁的4个必要条件

    1)互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
    2)请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
    3)不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
    4)环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。
  • 相关阅读:
    配置JAVA环境
    Sun Java认证
    JAVA中去掉空格
    2011年百度新闻热搜榜十大互联网人物
    23 设计模式
    HTML编辑器FCKeditor使用详解 [转]
    Linux 系统目录结构
    DotNet程序员是不是最不幸福?
    VS2005(c#)项目调试问题解决方案集锦 转
    详细介绍有关于.NET的委托
  • 原文地址:https://www.cnblogs.com/luoxiaofu/p/5250147.html
Copyright © 2011-2022 走看看