什么是锁?
- 在计算机科学中,锁是一种同步机制,用于在存在多线程的环境中实施对资源的访问限制。 就是在操作数据的时候,为了防止多个操作同时操作一个数据导致数据的错乱或者非即时而采用的一种规避手段。
常用的有以下几种:
1.@synchronized 互斥锁
2. NSLock 对象锁
3. dispatch_semaphore 信号量
4.NSCondition 条件锁
5.NSConditionLock
6.NSRecursiveLock 是递归锁
一.synchronized
synchronized是互斥锁,在这里主要考虑的是线程安全的问题,使用这个关键字,可以将一段代码限制在一个线程内使用,如果有一个线程正在使用这块资源,那么别的线程想要使用的时候就必须等待这快线程执行完毕!
首先 @synchronized() 小括号内需要一个参数,这个参数就表示信号量。这个参数可以是任何对象,包括 self,或者是自定义的信号量。针对不同的操作应该定义不同的信号量。
@synchronized() {…} 大括号中就是要加锁执行的代码,代码会操作一些数据。当开始执行代码时,意味着当前线程对其加锁了,当代码执行完后,自动解锁,其他线程才允许执行此段代码。
1.1优缺点
有点:能有效防止因多线程抢夺资源造成的数据安全问题。
缺点:需要消耗大量的CUP资源。
@interface ViewController ()
/** 票的总数 */
@property (nonatomic, assign) NSInteger ticketNumber;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.ticketNumber = 100;
for (NSInteger i = 0; i < 10; i++) {
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(sellTicketsWithSynchronized) object:nil];
[thread setName:[NSString stringWithFormat:@"售票员-%zd",i]];
[thread start];
}
}
- (void)sellTicketsWithSynchronized
{
while (true) {
@synchronized(self){
if (self.ticketNumber > 0) {
self.ticketNumber --;
NSThread *thread = [NSThread currentThread];
NSLog(@"%@卖了一张票,还剩%ld张票",[thread name],self.ticketNumber);
}else{
// 退出当前线程
[NSThread exit];
}
}
}
}
二. NSLock
NSLock 遵循 NSLocking 协议,lock 方法是加锁,unlock 是解锁,tryLock 是尝试加锁,如果失败的话返回 NO,lockBeforeDate: 是在指定Date之前尝试加锁,如果在指定时间之前都不能加锁,则返回NO。
@interface ViewController ()
/** 票的总数 */
@property (nonatomic, assign) NSInteger ticketNumber;
@property (strong, nonatomic) NSLock *lock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.ticketNumber = 100;
self.lock = [[NSLock alloc]init];
for (NSInteger i = 0; i < 10; i++) {
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(sellTicketsWithNSLock) object:nil];
[thread setName:[NSString stringWithFormat:@"售票员-%zd",i]];
[thread start];
}
}
- (void)sellTicketsWithNSLock
{
while (true) {
[self.lock lock];
if (self.ticketNumber > 0) {
self.ticketNumber --;
NSThread *thread = [NSThread currentThread];
NSLog(@"%@卖了一张票,还剩%ld张票",[thread name],self.ticketNumber);
}else{
// 退出当前线程
[NSThread exit];
}
[self.lock unlock]; // 解锁
}
}
三.dispatch_semaphore
dispatch_semaphore是GCD用来同步的一种方式,与他相关的共有三个函数,分别是
//创建信号量,参数:信号量的初值,如果小于0则会返回NULL
dispatch_semaphore_create(信号量值)
//提高信号量
dispatch_semaphore_signal(信号量)
//等待降低信号量
dispatch_semaphore_wait(信号量,等待时间)
(1)dispatch_semaphore_create的声明为:
dispatch_semaphore_t dispatch_semaphore_create(long value);
传入的参数为long,输出一个dispatch_semaphore_t类型且值为value的信号量。
值得注意的是,这里的传入的参数value必须大于或等于0,否则dispatch_semaphore_create会返回NULL。
(2)dispatch_semaphore_signal的声明为:
dispatch_semaphore_signal(semaphore);
这个函数会使传入的信号量semaphore的值加1;
(3) dispatch_semaphore_wait的声明为:
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
这个函数会使传入的信号量semaphore的值减1;这个函数的作用是这样的,如果semaphore信号量的值大于0,该函数所处线程就继续执行下面的语句,并且将信号量的值减1;如果semaphore的值为0,那么这个函数就阻塞当前线程等待timeout
-(void)dispatchSignal{
//crate的value表示,最多几个资源可访问
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任务1
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 1");
sleep(1);
NSLog(@"complete task 1");
dispatch_semaphore_signal(semaphore);
});
//任务2
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 2");
sleep(1);
NSLog(@"complete task 2");
dispatch_semaphore_signal(semaphore);
});
//任务3
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 3");
sleep(1);
NSLog(@"complete task 3");
dispatch_semaphore_signal(semaphore);
});
}
四. NSCondition
NSCondition 的对象实际上作为一个锁和一个线程检查器:锁主要为了当检测条件时保护数据源,执行条件引发的任务;线程检查器主要是根据条件决定是否继续运行线程,即线程是否被阻塞。
使用
NSConditon *condition =[ [NSCondition alloc]]init;
[condition lock];//一般用于多线程同时访问、修改同一个数据源,保证在同一时间内数据源只被访问、修改一次,其他线程的命令需要在lock 外等待,只到unlock ,才可访问
[condition unlock];//与lock 同时使用
[condition wait];//让当前线程处于等待状态
[condition signal];//CPU发信号告诉线程不用在等待,可以继续执行
NSCondition *lock = [[NSCondition alloc] init];
NSMutableArray *array = [[NSMutableArray alloc] init];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
while (!array.count) {
[lock wait];
}
[array removeAllObjects];
NSLog(@"array removeAllObjects");
[lock unlock];
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
[array addObject:@1];
NSLog(@"array addObject:@1");
[lock signal];
[lock unlock];
});
五. NSConditionLock
设计了一个例子,有一号售票窗口和二号售票窗口两个窗口可以买票,也会有一个退票窗口,但是退票窗口随机选择退到一号或者二号售票窗口。
NSConditionLock与NSCondition大体相同,但是NSConditionLock可以设置锁条件,而NSCondition确只是无脑的通知信号。
@interface ViewController ()
@property (nonatomic,strong) NSMutableArray *tickets;
@property (nonatomic,assign) int soldCount;
@property (nonatomic,strong) NSConditionLock *condition;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self forTest];
}
- (void)forTest
{
self.tickets = [NSMutableArray arrayWithCapacity:1];
self.condition = [[NSConditionLock alloc]initWithCondition:0];
NSThread *windowOne = [[NSThread alloc]initWithTarget:self selector:@selector(soldTicketOne) object:nil];
[windowOne start];
NSThread *windowTwo = [[NSThread alloc]initWithTarget:self selector:@selector(soldTicketTwo) object:nil];
[windowTwo start];
NSThread *windowTuiPiao = [[NSThread alloc]initWithTarget:self selector:@selector(tuiPiao) object:nil];
[windowTuiPiao start];
}
//一号窗口
-(void)soldTicketOne
{
while (YES) {
NSLog(@"====一号窗口没票了,等别人退票");
[self.condition lockWhenCondition:1];
NSLog(@"====在一号窗口买了一张票,%@",[self.tickets objectAtIndex:0]);
[self.tickets removeObjectAtIndex:0];
[self.condition unlockWithCondition:0];
}
}
//二号窗口
-(void)soldTicketTwo
{
while (YES) {
NSLog(@"====二号窗口没票了,等别人退票");
[self.condition lockWhenCondition:2];
NSLog(@"====在二号窗口买了一张票,%@",[self.tickets objectAtIndex:0]);
[self.tickets removeObjectAtIndex:0];
[self.condition unlockWithCondition:0];
}
}
- (void)tuiPiao
{
while (YES) {
sleep(3);
[self.condition lockWhenCondition:0];
[self.tickets addObject:@"南京-北京(退票)"];
int x = arc4random() % 2;
if (x == 1) {
NSLog(@"====有人退票了,赶快去一号窗口买");
[self.condition unlockWithCondition:1];
}else
{
NSLog(@"====有人退票了,赶快去二号窗口买");
[self.condition unlockWithCondition:2];
}
}
}
六.NSRecursiveLock
NSRecursiveLock 是递归锁,如果在递归中使用锁,可能会造成死锁的情况,但是若使用递归锁即可解决这个问题,递归锁允许一个线程多次请求锁,保证不会出现死锁的情况。
NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^recursiveBlock)(int);
recursiveBlock = ^(int value) {
[lock lock];
if (value > 0) {
NSLog(@"value is %d",value);
recursiveBlock(value-1);
}
[lock unlock];
};
recursiveBlock(5);
});