线程同步方案
- OSSpinLock 自旋锁
- os_unfair_lock
- pthread_mutex
- dispatch_semaphore 信号量
- dispatch_queue(DISPATCH_QUEUE_SERIAL) 串行队列
- NSLock
- NSRecursiveLock
- NSCondition
- NSConditionLock 条件锁
- @synchronized
卖票方法
#import "ViewController.h"
#import <libkern/OSAtomic.h> //导入头文件 自旋锁
@interface ViewController ()
@property (assign, nonatomic) OSSpinLock lock;
@end
/**
卖票演示
*/
- (void)ticketTest
{
self.ticketsCount = 15;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
}
/**
卖1张票
*/
- (void)saleTicket
{
// 加锁
OSSpinLockLock(&_lock);
int oldTicketsCount = self.ticketsCount;
sleep(.2);
oldTicketsCount--;
self.ticketsCount = oldTicketsCount;
NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
// 解锁
OSSpinLockUnlock(&_lock);
//尝试加锁 更加安全
// if (OSSpinLockTry(&_lock)) {
// int oldTicketsCount = self.ticketsCount;
// sleep(.2);
// oldTicketsCount--;
// self.ticketsCount = oldTicketsCount;
// NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
//
// OSSpinLockUnlock(&_lock);
// }
}
2018-09-12 09:49:28.048945+0800 Interview04-线程同步[986:58920] 还剩14张票 - <NSThread: 0x60400027d200>{number = 3, name = (null)}
2018-09-12 09:49:28.049229+0800 Interview04-线程同步[986:58920] 还剩13张票 - <NSThread: 0x60400027d200>{number = 3, name = (null)}
2018-09-12 09:49:28.049457+0800 Interview04-线程同步[986:58920] 还剩12张票 - <NSThread: 0x60400027d200>{number = 3, name = (null)}
2018-09-12 09:49:28.050802+0800 Interview04-线程同步[986:58920] 还剩11张票 - <NSThread: 0x60400027d200>{number = 3, name = (null)}
2018-09-12 09:49:28.050978+0800 Interview04-线程同步[986:58920] 还剩10张票 - <NSThread: 0x60400027d200>{number = 3, name = (null)}
2018-09-12 09:49:28.051562+0800 Interview04-线程同步[986:58917] 还剩9张票 - <NSThread: 0x60400027d780>{number = 4, name = (null)}
2018-09-12 09:49:28.051847+0800 Interview04-线程同步[986:58917] 还剩8张票 - <NSThread: 0x60400027d780>{number = 4, name = (null)}
2018-09-12 09:49:28.052481+0800 Interview04-线程同步[986:58917] 还剩7张票 - <NSThread: 0x60400027d780>{number = 4, name = (null)}
2018-09-12 09:49:28.052850+0800 Interview04-线程同步[986:58917] 还剩6张票 - <NSThread: 0x60400027d780>{number = 4, name = (null)}
2018-09-12 09:49:28.053069+0800 Interview04-线程同步[986:58917] 还剩5张票 - <NSThread: 0x60400027d780>{number = 4, name = (null)}
2018-09-12 09:49:28.194761+0800 Interview04-线程同步[986:58919] 还剩4张票 - <NSThread: 0x604000276140>{number = 5, name = (null)}
2018-09-12 09:49:28.195136+0800 Interview04-线程同步[986:58919] 还剩3张票 - <NSThread: 0x604000276140>{number = 5, name = (null)}
2018-09-12 09:49:28.195393+0800 Interview04-线程同步[986:58919] 还剩2张票 - <NSThread: 0x604000276140>{number = 5, name = (null)}
2018-09-12 09:49:28.195626+0800 Interview04-线程同步[986:58919] 还剩1张票 - <NSThread: 0x604000276140>{number = 5, name = (null)}
2018-09-12 09:49:28.195803+0800 Interview04-线程同步[986:58919] 还剩0张票 - <NSThread: 0x604000276140>{number = 5, name = (null)}
OSSpinLock总结
OSSpinLock叫做”自旋锁”,等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源
目前已经不再安全,可能会出现优先级反转问题
不安全,会根据优先级反转,优先级高的会先加锁!
优先级反转 如果线程2先进去,线程1的优先级比较高,那么CPU会更线程1分配大量资源,导致线程2资源很好,然后线程2就无法解锁,会导致死锁现象
os_unfair_lock
- os_unfair_lock用于取代不安全的OSSpinLock ,从iOS10开始才支持
- 从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等
- 需要导入头文件#import <os/lock.h>
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT
os_unfair_lock_trylock(&lock);//尝试加锁
os_unfair_lock_lock(& lock);//加锁
os_unfair_lock_unlock(& lock); //解锁
os_unfair_lock的特点等不到锁就休眠
pthread_mutex
- pthread开头的是垮平台的
- mutex叫做”互斥锁”,等待锁的线程会处于休眠状态
- 需要导入头文件#import <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_DEFAULT;
// #define PTHREAD_MUTEX_ERRORCHECK 1 //排错锁
// #define PTHREAD_MUTEX_RECURSIVE 2 //递归锁
// #define PTHREAD_MUTEX_DEFAULT //默认 PTHREAD_MUTEX_NORMAL
// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// 初始化锁
pthread_mutex_init(mutex, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);
- (void)ticket
{
pthread_mutex_lock(&_ticketMutex); //加锁
执行的代码
pthread_mutex_unlock(&_ticketMutex); //解锁
}
- 默认锁只能加一次锁
pthread_mutex 递归锁
pthread_mutex_t mutex = PTHREAD_MUTEX_RECURSIVE;
// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// 初始化锁
pthread_mutex_init(mutex, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);
//比如调用频繁这样调用递归
- (void)otherTest
{
pthread_mutex_lock(&_mutex);
NSLog(@"%s", __func__);
static int count = 0;
if (count < 10) {
count++;
[self otherTest];
}
pthread_mutex_unlock(&_mutex);
}
- 递归锁 允许同一线程可以多次加锁(不同线程 不允许这样就可以保护线程)
NSLock ,NSRecursiveLock
- NSLock是对mutex普通锁的封装
- NSRecursiveLock也是对mutex递归锁的封装,API跟NSLock基本一致
@property (strong, nonatomic) NSLock *ticketLock;
- (instancetype)init
{
if (self = [super init]) {
self.ticketLock = [[NSLock alloc] init];
}
return self;
}
- (void)saleTicket
{
[self.ticketLock lock]; 加锁
加锁的内容代码
[self.ticketLock unlock]; 解锁
}
NSCondition
- NSCondition是对mutex和cond的封装
/** 系统提供的方法*/
- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;
@property (strong, nonatomic) NSCondition *condition;
@property (strong, nonatomic) NSMutableArray *data;
- (instancetype)init
{
if (self = [super init]) {
self.condition = [[NSCondition alloc] init];
self.data = [NSMutableArray array];
}
return self;
}
// 生产者-消费者模式
// 线程1
// 删除数组中的元素
- (void)__remove
{
[self.condition lock];
NSLog(@"__remove - begin");
if (self.data.count == 0) {
// 等待
[self.condition wait];
}
[self.data removeLastObject];
NSLog(@"删除了元素");
[self.condition unlock];
}
// 线程2
// 往数组中添加元素
- (void)__add
{
[self.condition lock];
sleep(1);
[self.data addObject:@"Test"];
NSLog(@"添加了元素");
// 信号
[self.condition signal];
// 广播
// [self.condition broadcast];
[self.condition unlock];
}
NSConditionLock
- NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值
- 条件锁
/** 系统提供方法 */
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
现在我想执行__one 执行线程 __two __three
@property (strong, nonatomic) NSConditionLock *conditionLock;
- (instancetype)init
{
if (self = [super init]) {
self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];
}
return self;
}
- (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];
}
dispatch_queue(DISPATCH_QUEUE_SERIAL)
@property (strong, nonatomic) dispatch_queue_t ticketQueue;
- (instancetype)init
{
if (self = [super init]) {
self.ticketQueue = dispatch_queue_create("ticketQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)__saleTicket
{
dispatch_sync(self.ticketQueue, ^{
代码
});
}
dispatch_semaphore_t 信号量
- semaphore叫做”信号量”
- 信号量的初始值,可以用来控制线程并发访问的最大数量
- 信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步
// 信号量初始值
int value = 1;
//初始化信号量
@property (strong, nonatomic) dispatch_semaphore_t ticketSemaphore;
- (instancetype)init
{
if (self = [super init]) {
self.ticketSemaphore = dispatch_semaphore_create(1);
}
return self;
}
// 线程10、7、6、9、8
- (void)test
{
// 如果信号量的值 > 0,就让信号量的值减1,然后继续往下执行代码
// 如果信号量的值 <= 0,就会休眠等待,直到信号量的值变成>0,就让信号量的值减1,然后继续往下执行代码
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
sleep(2);
NSLog(@"test - %@", [NSThread currentThread]);
// 让信号量的值+1
dispatch_semaphore_signal(self.semaphore);
}