5000
, 取2500
, 因此应该剩3500
, 可是结果剩了2500
2000
, 这就是多线程的安全隐患问题, 是数据错乱10
张, 应该剩余0
张, 可是结果却剩余3
张, 说明数据出现了错乱OSSpinLock
os_unfair_lock
pthread_mutex
dispatch_semaphore
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSRecursiveLock
NSCondition
NSConditionLock
@synchronized
复制代码
多线程安全隐患示例01 – 存钱取钱
和多线程安全隐患示例02 – 卖票
代码封装到一个BaseDemo
类中, 具体代码以下图BaseDemo
暴露出五个方法, 两个测试调用, 三个线程调用AddLockDemo
继承自BaseDemo
ViewController
中代码以下OSSpinLock
叫作自旋锁
,等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源存钱取钱
和卖票
的安全隐患存钱取钱
和卖票
中加入OSSpinLock
OSSpinLock
目前已经再也不安全,可能会出现优先级反转问题thread1
、thread2
和thread3
thread1
执行一段时间后, 接着让thread2
执行一段时间, 而后再让thread3
执行一段时间假设经过OSSpinLock给两个线程`thread1`和`thread2`加锁
thread优先级高, thread2优先级低
若是thread2先加锁, 可是尚未解锁, 此时CPU切换到`thread1`
由于`thread1`的优先级高, 因此CPU会更多的给`thread1`分配资源, 这样每次`thread1`中遇到`OSSpinLock`都处于使用状态
此时`thread1`就会不停的检测`OSSpinLock`是否解锁, 就会长时间的占用CPU
这样就会出现相似于死锁的问题
复制代码
os_unfair_lock
用于取代不安全的OSSpinLock, 从iOS10
开始才支持os_unfair_lock
锁的线程会处于休眠状态, 并不是忙等#import <os/lock.h>
// 初始化
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
// 尝试加锁, 若是lcok已经被使用, 加锁失败返回false, 若是加锁成功, 返回true
os_unfair_lock_trylock(&lock);
// 加锁
os_unfair_lock_lock(&lock);
// 解锁
os_unfair_lock_unlock(&lock);
复制代码
存钱取钱
和卖票
的安全隐患os_unfair_lock
mutex
叫作互斥锁
,等待锁的线程会处于休眠状态#import <pthread.h>
// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// 初始化锁
pthread_mutex_t pthread;
pthread_mutex_init(&pthread, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);
// 销毁锁
pthread_mutex_destroy(&pthread);
复制代码
#define PTHREAD_MUTEX_NORMAL 0
#define PTHREAD_MUTEX_ERRORCHECK 1
#define PTHREAD_MUTEX_RECURSIVE 2
#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL
复制代码
存钱取钱
和卖票
的安全隐患PthreadTest
类继承自NSObject
, 其中recursive
是一个递归方法ViewController
中代码以下, 点击屏幕后调用PthreadTest
的recursive
方法recursive
中调用recursive
, 此时尚未解锁, 再次进行加锁, 因此发生了死锁pthread
初始化时的属性类型为PTHREAD_MUTEX_RECURSIVE
, 这样pthread
就是一把递归锁PthreadTest
中代码以下ViewController
中代码以下当点击屏幕时, 会在array
中移除最后一个元素
和添加一个新元素
, 代码中能够看到, 使用不一样线程调用__remove
和__add
两个方法安全
如今的需求是, 只有在array
不为空的状况下, 才能执行删除操做, 若是直接运行, 那么可能会先调用__remove
在调用__add
, 那么就与需求相违背bash
因此, 咱们可使用条件
对两个方法进行优化多线程
建立cond
函数
array.count == 0
时, 是程序进入休眠, 只有当array
中添加了新数据后在发起信号, 将休眠的线程唤醒__remove
方法, 可是却在__add
中添加新元素以后再移除元素NSLock
、NSRecursiveLock
、NSCondition
和NSConditionLock
是基于pthread
封装的OC对象AddLockDemo
中代码以下, 直接使用NSLock
进行加锁ViewController
中点击屏幕时调用方法GNUStep
中关于NSLock
的底层代码, 能够看到NSLock
是基础pthread
封装的normal
锁PthreadTest
中代码以下, 使用NSRecursiveLock
对递归函数
加锁解锁ViewController
中, 当点击屏幕时调用recursive
方法递归锁
的结果GNUStep
中关于NSRecursiveLock
的底层代码PthreadTest
中代码以下, 使用NSCondition
加锁解锁ViewController
中, 当点击屏幕时调用pthreadTest
方法__remove
方法, 可是却在__add
中给array
添加了新元素以后, 才删除一个元素
GNUStep
中关于NSCondition
的底层代码NSConditionLock
是对NSCondition
的进一步封装@interface NSConditionLock : NSObject <NSLocking> {
@private
void *_priv;
}
// 初始化, 同时设置 condition
- (instancetype)initWithCondition:(NSInteger)condition;
// condition值
@property (readonly) NSInteger condition;
// 只有NSConditionLock实例中的condition值与传入的condition值相等时, 才能加锁
- (void)lockWhenCondition:(NSInteger)condition;
// 尝试加锁
- (BOOL)tryLock;
// 尝试加锁, 只有NSConditionLock实例中的condition值与传入的condition值相等时, 才能加锁
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
// 解锁, 同时设置NSConditionLock实例中的condition值
- (void)unlockWithCondition:(NSInteger)condition;
// 加锁, 若是锁已经使用, 那么一直等到limit为止, 若是过期, 不会加锁
- (BOOL)lockBeforeDate:(NSDate *)limit;
// 加锁, 只有NSConditionLock实例中的condition值与传入的condition值相等时, 才能加锁, 时间限制到limit, 超时加锁失败
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
// 锁的name
@property (nullable, copy) NSString *name;
@end
复制代码
NSConditionLock
设置线程的执行顺序ViewController
代码以下dispatch_semaphore_t
设置信号量为1
, 来控制赞成之间只有一条线程能执行, 实际代码以下@synchronized
是对mutex
递归锁的封装objc4
中的objc-sync.mm
文件@synchronized(obj)
内部会生成obj
对应的递归锁,而后进行加锁、解锁操做@synchronized
进行加锁objc_sync_enter
和objc_sync_exit
两个函数, 分别用于加锁和解锁SyncData
recursive_mutex_tt
recursive_mutex_tt
, 能够看到底层是经过os_unfair_recursive_lock
封装的锁对象
获取锁的代码LIST_FOR_OBJ
, 点击查看对象
, 会获取惟一标识所谓锁性能从高到低排序
os_unfair_lock
OSSpinLock
dispatch_semaphore
pthread_mutex
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSCondition
pthread_mutex(recursive)
NSRecursiveLock
NSConditionLock
@synchronized
复制代码