多线程 (三)iOS中的锁

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

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

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

[email protected]:对象级别所,互斥锁,性能较差不推荐使用

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

这里写要加锁的代码

}

  @synchronized使用注意点

  1.加锁的代码尽量少

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

- (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:互斥锁,

@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:递归锁

@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条件

@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。

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

@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];
        //在i 为 1的时候阻塞线程1s
        if (i == 1)
        {
            [NSThread sleepForTimeInterval:1];
        }

    }
}

- (void)conditionLockAction2 {

    //当标识为2时同步代码段才能够执行,如果标识为其它数字则当前线程被阻塞。
    [_cdtLock lockWhenCondition:1];

    NSLog(@"thread2");

    [_cdtLock unlock];

}

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

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

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

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

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

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

- (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];
    }

}

另外发一张总结的图片

时间: 2024-10-07 05:42:48

多线程 (三)iOS中的锁的相关文章

多线程(三) iOS中的锁

锁的类别:互斥锁,递归锁,条件锁,自旋锁等 锁的实现方式:NSLock,NSRecursiveLock, NSConditionLock,@synchronized,GCD的信号量等 下面说一下常用的几种锁: [email protected]:对象级别所,互斥锁,性能较差不推荐使用 @synchronized(这里添加一个OC对象,一般使用self) { 这里写要加锁的代码 } @synchronized使用注意点 1.加锁的代码尽量少 2.添加的OC对象必须在多个线程中都是同一对象,下面举一

多线程(三) java中线程的简单使用

============================================= 原文链接:多线程(三) java中线程的简单使用 转载请注明出处! ============================================= java中,启动线程通常是通过Thread或其子类通过调用start()方法启动. 常见使用线程有两种:实现Runnable接口和继承Thread.而继承Thread亦或使用TimerTask其底层依旧是实现了Runnabel接口.考虑到java的

JAVA多线程(三) 线程池和锁的深度化

github演示代码地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/src/main/java/com/kawa/thread 1.线程池 1.1 线程池是什么 Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池能够带来3个好处. 第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 第二:提高响应速度.当任务

java多线程并发编程中的锁

synchronized: https://www.cnblogs.com/dolphin0520/p/3923737.html Lock:https://www.cnblogs.com/dolphin0520/p/3923167.html 原文地址:https://www.cnblogs.com/simpleDi/p/10462871.html

iOS开发笔记--iOS中的多线程

摘要 本文主要介绍iOS开发中的三种多线程技术:NSThread, NSOperation/NSOperationQueue, GCD.以及在多线程编程中的注意点和小技巧. 多线程 NSThread NSOperation/NSOperationQueue GCD 目录[-] iOS中的多线程 iOS的三种多线程技术特点: GCD基本思想 队列: 操作: 不同队列中嵌套同步操作dispatch_sync的结果: 同步操作dispatch_sync的应用场景: GCD优点: GCD队列: NSOp

深入理解 iOS 开发中的锁

来源:伯乐在线 - 夏天然后 链接:http://ios.jobbole.com/89474/ 点击 → 申请加入伯乐在线专栏作者 摘要 本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大量的实战经验,另一方面这样的文章相当多,比如 iOS中保证线程安全的几种方式与性能对比.iOS 常见知识点(三):Lock.本文也不会详细介绍锁的具体实现原理,这会涉及到太多相关知识,笔者不敢误人子弟. 本文要做的就是简单的分析 iOS 开发中常见的几种锁如何实现,以及优缺点是什么,为什么会有性能上

Java并发编程(3) JUC中的锁

一 前言 前面已经说到JUC中的锁主要是基于AQS实现,而AQS(AQS的内部结构 .AQS的设计与实现)在前面已经简单介绍过了.今天记录下JUC包下的锁是怎么基于AQS上实现的 二 同步锁 同步锁不是JUC中的锁但也顺便提下,它是由synchronized 关键字进行同步,实现对竞争资源互斥访问的锁. 同步锁的原理:对于每一个对象,有且仅有一个同步锁:不同的线程能共同访问该同步锁.在同一个时间点该同步锁能且只能被一个线程获取到,其他线程都得等待. 另外:synchronized是Java中的关

ios中的几种多线程实现

iOS 支持多个层次的多线程编程,层次越高的抽象程度越高,使用起来也越方便,也是苹果最推荐使用的方法.下面根据抽象层次从低到高依次列出iOS所支持的多线程编程范式:1, Thread;2, Cocoa operations;3, Grand Central Dispatch (GCD) (iOS4 才开始支持)下面简要说明这三种不同范式:Thread 是这三种范式里面相对轻量级的,但也是使用起来最负责的,你需要自己管理thread的生命周期,线程之间的同步.线程共享同一应用程序的部分内存空间,它

iOS中你必须了解的多线程

多线程概念详解 什么是进程? 简单的说进程就是我们电脑上运行的一个个应用程序,每一个程序就是一个进程,并且每个进程之间是独立的,每个进程运行在其专用受保护的内存空间内(window系统可以通过任务管理器进行查看,Mac系统中可以通过活动监视器对其进行查看) 什么是线程? 通过上面的介绍我们知道了什么是进程,那么如何让进程运行起来,这个时候就要有线程了,也就是说每个应用程序想要跑起来,最少也要有一条线程存在,其实应用程序启动的时候我们的系统就会默认帮我们的应用程序开启一条线程,这条线程也叫做'主线