前言:一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,比如多个线程访问同一个对象、同一个变量、同一个文件和同一个方法等。因此当多个线程访问同一块资源时,很容易会发生数据错误及数据不安全等问题。因此要避免这些问题,我们需要使用“线程锁”来实现。
1) @synchronized(互斥锁)
优点:使用 @synchronized 关键字可以很方便地创建锁对象,而且不用显式的创建锁对象。
缺点:会隐式添加一个异常处理来保护代码,该异常处理会在异常抛出的时候自动释放互斥锁。而这种隐式的异常处理会带来系统的额外开销,为优化资源,你可以使用锁对象。
二、“Object-C”语言
1)NSLock(互斥锁)
2) NSRecursiveLock(递归锁)
条件锁,递归或循环方法时使用此方法实现锁,可避免死锁等问题。
3) NSConditionLock(条件锁)
使用此方法可以指定,只有满足条件的时候才可以解锁。
4)NSDistributedLock(分布式锁)
在IOS中不需要用到,也没有这个方法,因此本文不作介绍,这里写出来只是想让大家知道有这个锁存在。
如果想要学习NSDistributedLock的话,你可以创建MAC OS的项目自己演练,方法请自行Google,谢谢。
三、 C 语言
1) pthread_mutex_t(互斥锁)
2) GCD-信号量(“互斥锁”)
3) pthread_cond_t(条件锁)
一、使用关键字 :
// 实例类person Person *person = [[Person alloc] init]; // 线程A dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @synchronized(person) { [person personA]; [NSThread sleepForTimeInterval:3]; // 线程休眠3秒 } }); // 线程B dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @synchronized(person) { [person personB]; } });
关键字 @synchronized 的使用,锁定的对象为锁的唯一标识,只有标识相同时,才满足互斥。如果线程 B 锁对象 person 改为 self 或其它标识,那么线程 B 将不会被阻塞。
// 实例类person Person *person = [[Person alloc] init]; // 创建锁 NSLock *myLock = [[NSLock alloc] init]; // 线程A dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [myLock lock]; [person personA]; [NSThread sleepForTimeInterval:5]; [myLock unlock]; }); // 线程B dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [myLock lock]; [person personB]; [myLock unlock]; });
程序运行结果:线程 B 会等待线程 A 解锁后,才会去执行线程 B 。如果线程 B 把 lock 和 unlock 方法去掉之后,则线程 B 不会被阻塞,这个和 synchronized 的一样,需要使用同样的锁对象才会互斥。
NSLock 类还提供 tryLock 方法,意思是尝试锁定,当锁定失败时,不会阻塞进程,而是会返回 NO 。你也可以使用 lockBeforeDate: 方法,意思是在指定时间之前尝试锁定,如果在指定时间前都不能锁定,也是会返回 NO 。
注意:锁定(lock)和解锁(unLock)必须配对使用
2)使用 NSRecursiveLock 类实现锁
// 实例类person Person *person = [[Person alloc] init]; // 创建锁对象 NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init]; // 创建递归方法 static void (^testCode)(int); testCode = ^(int value) { [theLock tryLock]; if (value > 0) { [person personA]; [NSThread sleepForTimeInterval:1]; testCode(value - 1); } [theLock unlock]; }; //线程A dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ testCode(5); }); //线程B dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [theLock lock]; [person personB]; [theLock unlock]; });
如果我们把 NSRecursiveLock类换成NSLock类,那么程序就会死锁。因为在此例子中,递归方法会造成锁被多次锁定(Lock),所以自己也被阻塞了。而使用NSRecursiveLock类,则可以避免这个问题。
3)使用 NSConditionLock(条件锁)类实现锁:
使用此方法可以创建一个指定开锁的条件,只有满足条件,才能开锁。
// 实例类person Person *person = [[Person alloc] init]; // 创建条件锁 NSConditionLock *conditionLock = [[NSConditionLock alloc] init]; // 线程A dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [conditionLock lock]; [person personA]; [NSThread sleepForTimeInterval:5]; [conditionLock unlockWithCondition:10]; }); // 线程B dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [conditionLock lockWhenCondition:10]; [person personB]; [conditionLock unlock]; });
线程A使用的是lock方法,因此会直接进行锁定,并且指定了只有满足10的情况下,才能成功解锁。
unlockWithCondition:方法,创建条件锁,参数传入“整型”。lockWhenCondition:方法,则为解锁,也是传入一个“整型”的参数。
1)使用 pthread_mutex_t 实现锁
注意:必须在头文件导入:#import <pthread.h>
// 实例类person Person *person = [[Person alloc] init]; // 创建锁对象 __block pthread_mutex_t mutex; pthread_mutex_init(&mutex, NULL); // 线程A dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ pthread_mutex_lock(&mutex); [person personA]; [NSThread sleepForTimeInterval:5]; pthread_mutex_unlock(&mutex); }); // 线程B dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ pthread_mutex_lock(&mutex); [person personB]; pthread_mutex_unlock(&mutex); });
实现效果和上例的相一致
2) 使用 GCD 实现 “ 锁 ” (信号量)
GCD提供一种信号的机制,使用它我们可以创建“锁”(信号量和锁是有区别的,具体请自行百度)。
// 实例类person Person *person = [[Person alloc] init]; // 创建并设置信量 dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); // 线程A dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); [person personA]; [NSThread sleepForTimeInterval:5]; dispatch_semaphore_signal(semaphore); }); // 线程B dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); [person personB]; dispatch_semaphore_signal(semaphore); });
效果也是和上例介绍的相一致。
我在这里解释一下代码。dispatch_semaphore_wait方法是把信号量加1,dispatch_semaphore_signal是把信号量减1。
我们把信号量当作是一个计数器,当计数器是一个非负整数时,所有通过它的线程都应该把这个整数减1。如果计数器大于0,那么则允许访问,并把计数器减1。如果为0,则访问被禁止,所有通过它的线程都处于等待的状态。
// 实例类person Person *person = [[Person alloc] init]; // 创建互斥锁 __block pthread_mutex_t mutex; pthread_mutex_init(&mutex, NULL); // 创建条件锁 __block pthread_cond_t cond; pthread_cond_init(&cond, NULL); // 线程A dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ pthread_mutex_lock(&mutex); pthread_cond_wait(&cond, &mutex); [person personA]; pthread_mutex_unlock(&mutex); }); // 线程B dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ pthread_mutex_lock(&mutex); [person personB]; [NSThread sleepForTimeInterval:5]; pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); });
效果:程序会首先调用线程B,在5秒后再调用线程A。因为在线程A中创建了等待条件锁,线程B有激活锁,只有当线程B执行完后会激活线程A。
pthread_cond_wait方法为等待条件锁。
pthread_cond_signal方法为激动一个相同条件的条件锁。
一般来说,如果项目不大,我们都会偷点懒,直接使用关键字 @synchronized建立锁,懒人方法。其次可以使用苹果提供的OC方法,最后才会去使用C去建立锁。
本文参考文章:
iOS多线程开发(四)---线程同步
Objective-C中不同方式实现锁(一)
信号量与互斥锁
博文作者:GarveyCalvin
博文出处: http://www.cnblogs.com/GarveyCalvin/
本文版权归作者和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作!