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

锁的实现方式: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占用的资源。

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

  1. 谈谈iOS中的锁

    1 前言 近日工作不是太忙,刚好有时间了解一些其他东西,本来打算今天上午去体检,但是看看天气还是明天再去吧,也有很大一个原因:就是周六没有预约上!闲话少说,这里简单对锁来个简单介绍分享. 2 目录 第 ...

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

    java中,启动线程通常是通过Thread或其子类通过调用start()方法启动. 常见使用线程有两种:实现Runnable接口和继承Thread.而继承Thread亦或使用TimerTask其底层依 ...

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

    github演示代码地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/spb-brian-query-servic ...

  4. 多线程 (三)iOS中的锁

    锁的类别:互斥锁,递归锁,条件锁,自旋锁等 锁的实现方式:NSLock,NSRecursiveLock, NSConditionLock,@synchronized,GCD的信号量等 下面说一下常用的 ...

  5. java多线程并发编程中的锁

    synchronized: https://www.cnblogs.com/dolphin0520/p/3923737.html Lock:https://www.cnblogs.com/dolphi ...

  6. 深入理解 iOS 开发中的锁

    来源:伯乐在线 - 夏天然后 链接:http://ios.jobbole.com/89474/ 点击 → 申请加入伯乐在线专栏作者 摘要 本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大 ...

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

    一 前言 前面已经说到JUC中的锁主要是基于AQS实现,而AQS(AQS的内部结构 .AQS的设计与实现)在前面已经简单介绍过了.今天记录下JUC包下的锁是怎么基于AQS上实现的 二 同步锁 同步锁不 ...

  8. iOS中的几种锁的总结,三种开启多线程的方式(GCD、NSOperation、NSThread)

    学习内容 欢迎关注我的iOS学习总结--每天学一点iOS:https://github.com/practiceqian/one-day-one-iOS-summary OC中的几种锁 为什么要引入锁 ...

  9. 多线程在iOS开发中的应用

    多线程基本概念 01 进程 进程是指在系统中正在运行的一个应用程序.每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内. 02 线程 2-1 基本概念 1个进程要想执行任务,必须得有线程 ...

随机推荐

  1. Xshell 初次应用

    以前就想安装Xshell,今天终于弄好了,可以在windows下对Linux服务端进行管理. 关于SSH和Xshell的介绍见参考,Linux上安装的是ssh服务端,所以咱们如果希望通过远程访问的方式 ...

  2. 下载SCI论文

    今天帮汪博下了一下午SCI论文,见到了很多不知道的网站.顺便了解了一下: 首先进学校图书馆的网站,然后选择    WOS核心合集(含SCIE.SSCI.A&HCI.CPCI数据库) ---&g ...

  3. 2017.2.21 activiti实战--第十三章--流量数据查询与跟踪(一)查询接口介绍及运行时数据查询

    学习资料:<Activiti实战> 第十三章 流量数据查询与跟踪 本章讲解运行时与历史数据的查询方法.主要包含三种:标准查询,Native查询,CustomSql查询. 13.1 Quer ...

  4. 第十二题 Merge Sorted Array

    Given two sorted integer arrays A and B, merge B into A as one sorted array. Note: You may assume th ...

  5. SAS学习笔记之函数应用

    今天在做数据需求的时候遇到一些问题,因为不能够在数据库里面做,仅仅好在SAS里面实现.这就遇到了一些麻烦,须要使用一些函数实现部分功能,如查找字段中某个特殊字符出现的次数,查找某个字符的位置等,以下一 ...

  6. WPF 基础到企业应用系列5——WPF千年轮回 续前缘

    一.摘要 首先非常高兴这个系列能得到大家的关注和支持,前端时间身体状况不适,所以暂停了更新,对此表示非常抱歉,以后会逐渐加快进度.只是因为这是一个非常长的系列,我也想把它写好,所以以后也会慢慢来,在这 ...

  7. gray-code——找规律

    The gray code is a binary numeral system where two successive values differ in only one bit. Given a ...

  8. Node.js学习笔记(3)——关于回调函数和函数的回调

    说明:本人是node.js的初学者,尝试向别人解释这是怎么回事是自我学习的一个好方法.如果你发现有些地方并不是那么正确,欢迎提出来让我知道以便修正,共同进步,谢过^_^.       欢迎交流,本人微 ...

  9. 3、C++新的关键字

        C++ 添加了一些全新的关键字. 1.new     new 来进行动态内存的分配,而delect 则是进行内存的释放, 申请的方式: 变量申请: int *p = new int; // 申 ...

  10. ubuntu下编译原生ffmpeg

    本文主要介绍Linux 系统下如何编译Ffmpeg,编译环境是Ubuntu 16.04,Ffmpeg版本是3.4.2.Windows环境 下如何编译ffmpeg前面有博文介绍,也录有视频,感兴趣的同学 ...