天天看点

iOS多线程中的锁

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

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

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

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

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

iOS多线程中的锁

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

iOS多线程中的锁

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

}

 打印结果:

iOS多线程中的锁

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

iOS多线程中的锁

和我们预想的在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];

//在i 为 1的时候阻塞线程1s

if

(i == 1)

{

[

NSThread

sleepForTimeInterval:1];

}

}

}

- (

void

)conditionLockAction2 {

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

[_cdtLock lockWhenCondition:1];

NSLog

(@

"thread2"

);

[_cdtLock unlock];

}

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

iOS多线程中的锁

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

继续阅读