首页 技术 正文
技术 2022年11月14日
0 收藏 333 点赞 4,457 浏览 7965 个字

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

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

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

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

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

这里写要加锁的代码

}

  @synchronized使用注意点

  1.加锁的代码尽量少

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

12345678910111213141516171819202122232425262728293031323334 - (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在不同线程中就不同了;

多线程(三)  iOS中的锁

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

多线程(三)  iOS中的锁

2.NSLock:互斥锁,

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

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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 @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];     }

打印结果:多线程(三)  iOS中的锁

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

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

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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253 @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];     }

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

多线程(三)  iOS中的锁

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

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

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

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

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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546 - (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占用的资源。

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:8,954
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,479
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,291
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,108
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,740
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,774