一般会在多线程里面引出锁相关的面试题,或者直接问,反正没啥区别
锁的出现就是为了应付多线程的弊端,也就是多个线程访问同一个资源,举个例子你在使用的时候另一个线程也在修改,当你再次访问这个变量的结果会发生差异,这种导致最终结果至少不是想要的结果
常见的锁:
OSSpinLock,os_unfair_lock 前者已经废弃,替换者是后者,是自旋锁
pthread_mutex,NSLock,NSRecursiveLock,NSCondition,NSConditionLockpthread_mutex一般不使用,会使用它的封装对像NSLock,这些是互斥锁
@synchronized swift没有,只能用底层来实现各类似的,是一种最简单的互斥锁,但swift无法使用,仅存的优势也不在了
还有一些可以当锁用的技术:
串行队列 不赘述了,处于同一队列肯定得一个个访问
信号量限制 同一时间的最大访问量,设置为1也能起到锁的作用
atomic 不建议用,后面单独说下
先举个多线程导致结果错误的例子:
var money:Int = 100 func saveMoney() { var oldMoney: Int = self.money sleep(UInt32(0.2)) //处理数据 oldMoney += 60 self.money = oldMoney print("加60个币,还剩\(oldMoney)币 - \(Thread.current)") } func drawMoney() { var oldMoney: Int = self.money sleep(UInt32(0.3)) //处理数据 oldMoney -= 10 self.money = oldMoney print("减去10个币,还剩\(oldMoney)币 - \(Thread.current)") } override func viewDidLoad() { DispatchQueue.global().async{ for _ in 0..<10 { self.saveMoney() } } DispatchQueue.global().async{ for _ in 0..<10 { self.drawMoney() }}
我这边跑出的最终结果是480,这明显是个错误的结果,不同的编译环境和电脑,这个结果也许不会相同,所以不必在意这个结果,只要确定是错的,就没问题了
os_unfair_lock
这是一个自旋锁,等待的线程会不停的循环
var lock = os_unfair_lock()DispatchQueue.global().async{ for _ in 0..<10 { os_unfair_lock_lock(&lock) self.saveMoney() os_unfair_lock_unlock(&lock) }}DispatchQueue.global().async{ for _ in 0..<10 { os_unfair_lock_lock(&lock) self.drawMoney() os_unfair_lock_unlock(&lock) }}
其实也简单,在会冲突的地方加锁就好了,同一个变量,肯定是同一把锁
加60个币,还剩600币 – {number = 3, name = (null)}
log输出的结果就没问题了
NSLock
是一种互斥锁,这种锁不会循环等待,会直接休眠
let lock = NSLock()DispatchQueue.global().async{ for _ in 0..<10 { lock.lock() self.saveMoney() lock.unlock() }}DispatchQueue.global().async{ for _ in 0..<10 { lock.lock() self.drawMoney() lock.unlock() }}
单纯从使用上来说其实没有什么太大区别
NSRecursiveLock
递归锁,也是封装的mutex的递归锁,听名字就知道是递归用的,作用就是允许单个线程的重复加锁
var num:Int = 100var lock:NSLock = NSLock()func test(){ lock.lock() if self.num > 1 { self.num -= 10 print(self.num) self.test() } lock.unlock()}
比如在递归里面,这样加锁unlock不可能有执行的时候,所以就卡在那了,这种情况有很多种解决方式递归锁就是其中一种
var num:Int = 100var lock:NSRecursiveLock = NSRecursiveLock()func test(){ lock.lock() if self.num > 1 { self.num -= 10 print(self.num) self.test() } lock.unlock()}
需要注意的点是,递归锁,允许单一线程给一把锁重复加锁
NSCondition
条件锁,是对mutex和cont的封装,相对于其它锁来说,这个锁用于线程间通讯更方便一些,用于普通的锁反而是浪费了点
var arr:[String] = []var lock:NSCondition = NSCondition()func task1(){ lock.lock() /* 其它代码 */ if self.arr.count != 10{ lock.wait() } for i in self.arr{ print(i) } lock.unlock() /* 其它代码 */}func task2(){ /* 其它代码 */ for i in 0..<10 { self.arr.append(String(i)) } lock.signal() /* 其它代码 */}override func viewDidLoad() { DispatchQueue.global().async { //线程A /* 其它代码 */ self.task1() /* 其它代码 */ } DispatchQueue.global().async { //线程B /* 其它代码 */ self.task2() /* 其它代码 */ }}
比如A线程的task1执行需要B线程task2处理好的数据才能执行,那么用condition就能很轻松的完成线程中的通讯
NSConditionLock
比NSCondition要更加强大,多了个条件值,可以定制多个条件,实现更复杂的线程间通讯
var lock:NSConditionLock = NSConditionLock(condition: 1)lock.lock(whenCondition: 1)lock.unlock(withCondition: 2)lock.lock(whenCondition: 2)lock.unlock(withCondition: 3)
具体就不详细赘述了,有一个条件值,只有满足条件才会继续往下走,起始值就是初始化时候的值,每次解锁重新指定新的值
@synchronized
这个只支持OC,不支持Swift
@synchronized(self){ [self drawMoney];}
用自身加锁,不用再创建锁对象,是OC里面最简单的加锁方式,所以使用很频繁