多线程安全隐患解决方案
1.解决方案:使用线程同步技术(协同步调,按预定的先后次序进行)
2.常用的线程同步技术:加锁
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叫做自旋锁,等待锁的线程会处于忙等(busy-wait)状态,一直占用CPU资源
3.1.2 目前已经不安全,可能会出现优先级反转问题
3.1.3 如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法 释放锁
3.1.4 头文件 #import
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开始支持
3.2.2从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等
3.2.3头文件#import
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叫做"互斥锁 ",等待锁的线程会处于休眠状态
3.2.2头文件#import
3.2.3示例代码如下
// 静态初始化
// 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);
// // 销毁属性
// pthread_mutexattr_destroy(&attr);
// 初始化属性
// pthread_mutexattr_t attr;
// pthread_mutexattr_init(&attr);
// pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// 初始化锁
pthread_mutex_init(mutex, NULL);
// 销毁属性
// pthread_mutexattr_destroy(&attr);
3.4 NSLock | NSRecursiveLock
3.4.1.NSLock是对mutex普通锁的封装
3.4.2.NSRecursiveLock也是对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是对mutex和cond的封装
3.5.2常见API
- (void)wait; 让当前线程处于等待状态
- (BOOL)waitUntilDate:(NSDate *)limit;让当前线程在一定时间内处于等待状态
- (void)signal;CPU发信号告诉当前线程不用在等待,可以继续执行
- (void)broadcast;CPU发信号告诉所有线程不用在等待,可以继续执行
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(), ^{
// 信号
[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示例代码如下 放到串行队列里面
3.8 dispatch_semaphore_t
3.8.1semaphore叫做信号量
3.8.2信号量的初始值,可以用来控制线程并发访问的最大数量 不可以未负数
3.8.3信号量的初始值为1 ,代表同时只允许1条线程访问资源,保证线程同步
3.8.4示例代码如下
// 初始化信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
// 如果信号量=0当前线程就会进入休眠(直到信号量的值>0)
// 如果信号量>0 就减1 然后继续往下执行后面的代码
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 让信号量的值加1
dispatch_semaphore_signal(semaphore);
3.8 @synchronized
3.8.1 @synchronized 是对mutex递归锁的封装
3.8.2 @synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作
3.8.3 示例代码
@synchronized(obj){
}
练习代码git
https://gitee.com/tianmingfu/thread-synchronization.git