多線程安全隱患解決方案
1.解決方案:使用線程同步技術(shù)(協(xié)同步調(diào)誊抛,按預(yù)定的先后次序進(jìn)行)
2.常用的線程同步技術(shù):加鎖
3.iOS中的線程同步方案
OSSpinLock
os_unfair_lock
pthread_mutex
dispatch_semphore
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSRecursiveLock
NSCondition
NSConditionLock
@synchronized
3.1OSSpinLock
3.1.1OSSpinLock叫做自旋鎖,等待鎖的線程會(huì)處于忙等(busy-wait)狀態(tài),一直占用CPU資源
3.1.2 目前已經(jīng)不安全挥吵,可能會(huì)出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn)問(wèn)題
3.1.3 如果等待鎖的線程優(yōu)先級(jí)較高,它會(huì)一直占用著CPU資源花椭,優(yōu)先級(jí)低的線程就無(wú)法 釋放鎖
3.1.4 頭文件 #import <libkern/OSAtomic.h>
3.1.5 示例代碼如下
- (void)p_saleTicket {
//初始化
static OSSpinLock ossPinLock = OS_SPINLOCK_INIT;
// 嘗試加鎖(如果需要等待就不加鎖,直接返回false房午,如果不需要等待就加鎖返回true)
//bool result = OSSPinLockTry(&ossPinLock);
//加鎖
OSSpinLockLock(&ossPinLock);
sleep(1);
int oldCount = self.ticketsCount;
oldCount--;
self.ticketsCount = oldCount;
NSLog(@"余票%d",oldCount);
//解鎖
OSSpinLockUnlock(&ossPinLock);
}
3.2 os_unfair_lock
3.2.1 os_unfair_lock用于取代不安全的OSSPinLock矿辽,iOS10開(kāi)始支持
3.2.2從底層調(diào)用看,等待os_unfair_lock鎖的線程會(huì)處于休眠狀態(tài)郭厌,并非忙等
3.2.3頭文件#import<os/lock.h>
3.2.4示例代碼如下
- (void)p_saleTicket3 {
//初始化
static os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
//嘗試加鎖
//os_unfair_lock_trylock(&lock);
//加鎖
os_unfair_lock_lock(&lock);
sleep(1);
int oldCount = self.ticketsCount;
oldCount--;
self.ticketsCount = oldCount;
NSLog(@"余票%d",oldCount);
//解鎖
os_unfair_lock_unlock(&lock);
}
3.3 pthread_mutex_t
3.2.1 mutex叫做"互斥鎖 ",等待鎖的線程會(huì)處于休眠狀態(tài)
3.2.2頭文件#import <pthread/pthread.h>
3.2.3示例代碼如下
// 靜態(tài)初始化
// pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// // 初始化屬性
// pthread_mutexattr_t attr;
// pthread_mutexattr_init(&attr);
// pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// // 初始化鎖
// pthread_mutex_init(mutex, &attr);
// // 銷(xiāo)毀屬性
// pthread_mutexattr_destroy(&attr);
// 初始化屬性
// pthread_mutexattr_t attr;
// pthread_mutexattr_init(&attr);
// pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// 初始化鎖
pthread_mutex_init(mutex, NULL);
// 銷(xiāo)毀屬性
// pthread_mutexattr_destroy(&attr);
3.4 NSLock | NSRecursiveLock
3.4.1.NSLock是對(duì)mutex普通鎖的封裝
3.4.2.NSRecursiveLock也是對(duì)mutex遞歸鎖的封裝
3.4.3示例代碼如下
- (void)p_saleTicket {
[self.lock lock];
sleep(1);
int oldCount = self.ticketsCount;
oldCount--;
self.ticketsCount = oldCount;
NSLog(@"余票%d",oldCount);
[self.lock unlock];
}
3.5 NSCondition
3.5.1NSCondition是對(duì)mutex和cond的封裝
3.5.2常見(jiàn)API
- (void)wait; 讓當(dāng)前線程處于等待狀態(tài)
- (BOOL)waitUntilDate:(NSDate *)limit;讓當(dāng)前線程在一定時(shí)間內(nèi)處于等待狀態(tài)
- (void)signal;CPU發(fā)信號(hào)告訴當(dāng)前線程不用在等待袋倔,可以繼續(xù)執(zhí)行
- (void)broadcast;CPU發(fā)信號(hào)告訴所有線程不用在等待,可以繼續(xù)執(zhí)行
3.5.3示例代碼如下
- (void)p_saleTicket {
[self.lock lock];
if (self.ticketsCount == 12) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 信號(hào)
[self.lock signal];
});
// 等待
[self.lock wait];
}
sleep(1);
int oldCount = self.ticketsCount;
oldCount--;
self.ticketsCount = oldCount;
NSLog(@"余票%d",oldCount);
[self.lock unlock];
}
3.6 NSConditionLock
3.6.1示例代碼如下
self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];
- (void)otherTest
{
[[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__three) object:nil] start];
}
- (void)__one
{
[self.conditionLock lock];
NSLog(@"__one");
sleep(1);
[self.conditionLock unlockWithCondition:2];
}
- (void)__two
{
[self.conditionLock lockWhenCondition:2];
NSLog(@"__two");
sleep(1);
[self.conditionLock unlockWithCondition:3];
}
- (void)__three
{
[self.conditionLock lockWhenCondition:3];
NSLog(@"__three");
[self.conditionLock unlock];
}
3.7 DISPATCH_QUEUE_SERIAL
3.7.4示例代碼如下 放到串行隊(duì)列里面
3.8 dispatch_semaphore_t
3.8.1semaphore叫做信號(hào)量
3.8.2信號(hào)量的初始值折柠,可以用來(lái)控制線程并發(fā)訪問(wèn)的最大數(shù)量 不可以未負(fù)數(shù)
3.8.3信號(hào)量的初始值為1 宾娜,代表同時(shí)只允許1條線程訪問(wèn)資源,保證線程同步
3.8.4示例代碼如下
// 初始化信號(hào)量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
// 如果信號(hào)量=0當(dāng)前線程就會(huì)進(jìn)入休眠(直到信號(hào)量的值>0)
// 如果信號(hào)量>0 就減1 然后繼續(xù)往下執(zhí)行后面的代碼
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 讓信號(hào)量的值加1
dispatch_semaphore_signal(semaphore);
3.8 @synchronized
3.8.1 @synchronized 是對(duì)mutex遞歸鎖的封裝
3.8.2 @synchronized(obj)內(nèi)部會(huì)生成obj對(duì)應(yīng)的遞歸鎖扇售,然后進(jìn)行加鎖前塔、解鎖操作
3.8.3 示例代碼
@synchronized(obj){
}
練習(xí)代碼git
https://gitee.com/tianmingfu/thread-synchronization.git