锁的原理随笔

synchronized, NSLock, 递归锁, 条件锁

图中锁的性能从高到底依次是:OSSpinLock(自旋锁) -> dispatch_semaphone(信号量) -> pthread_mutex(互斥锁) -> NSLock(互斥锁) -> NSCondition(条件锁) -> pthread_mutex(recursive 互斥递归锁) -> NSRecursiveLock(递归锁) -> NSConditionLock(条件锁) -> synchronized(互斥锁)

自旋锁(OSSpinLock, atomic) -> 线程会反复检查变量是否可用 -> 忙等待 ->
会一直保持该锁 ->对于线程只会阻塞很短时间的场合是有效的.

互斥锁 (@synchronized , NSLock, pthread_mutex) -> 多线程编程 -> 防止两条线程同时对同一公共资源(例如全局变量)进行读写的机制 ->将代码切成一个个临界区.

条件锁 (NSCondition, NSConditionLock) ->条件变量 -> 资源要求不满足 -> 进入休眠, 资源被分配到->条件锁打开.

递归锁(pthread_mutex(recursive), NSRecursiveLock) ->同一个线程可以加锁N次而不会引发死锁 -> 特殊的互斥锁,即是带有递归性质的互斥锁

信号量(dispatch_semaphore) -> 更高级的同步机制 -> 互斥锁可以说是semaphore在仅取值0/1时的特例 ->实现更加复杂的同步

读写锁(特殊的自旋锁) -> 特殊的自旋锁 -> 一个读写锁同时只能有一个写者或者多个读者

  1. OSSpinLock -> 自旋锁之所以不安全,是因为获取锁后,线程会一直处于忙等待,造成了任务的优先级反转。->废弃 -> os_unfair_lock (加锁,休眠)
  2. atomic -> reallySetProperty -> spinlock_t加锁 -> 底层调用os_unfair_lock加锁 -> 防止哈希冲突 -> 加盐
  3. synchronized ->objc_sync_enter和objc_sync_exit
  • 哈希表 ->SyncList -> 多线程
  • SyncData - >链表 ->当前可重入
  • tls线程缓存, cache缓存
  • lockCount, threadCount ->递归互斥锁
    总结:
  1. @synchronized -> 递归锁
  2. @synchronized ->可重入(lockCount , threadCount) -> 可嵌套
  3. @synchronized -> 链表
  4. 链表查询, 缓存查找以及递归 -> 耗内存 -> 耗性能 -> 性能低
  5. 方便简单, 不用解锁,使用频率高
  6. 不能对非OC对象加锁
  7. @synchronized (self) ->嵌套次数较少的场景 -> 锁住的对象也并不永远是self
    8 嵌套次数较多 -> 锁self过多 -> 使用NSLock ,信号量

4. NSLock

NSLock -> 下层pthread_mutex的封装 -> 符号断点 -> Foundation ,但是不开源 ->借助Swift开源的Foundation分析

NSLock的性能仅次于pthead_mutex

5. pthread_mutex

互斥锁本身 -> 阻塞线程并睡眠

6. NSRecursiveLock

底层 -> pthread_mutex的封装 -> Swift的Foundation研究
NSLock 和 NSRecursiveLock底层几乎一样 ->NSRecursiveLock -> init -> 标识符PTHREAD_MUTEX_RECURSIVE

递归锁 -> 解决一种嵌套形式 -> 循环嵌套

7. NSCondition

条件锁 -> 锁和线程检查器
锁 -> 检查条件,保护数据源, 执行条件引发的任务
线程检查器 -> 根据条件是否运行线程
底层 -> Swift, Foundation -> pthread_mutex的封装

8. NSConditionLock

条件锁, 一个线程获得锁, 其他线程都要等待
NSConditionLock -> NSCondition + Lock

总结

OSSpinLock自旋锁由于安全性问题,在iOS10之后已经被废弃,其底层的实现用os_unfair_lock替代

使用OSSpinLock及所示,会处于忙等待状态

而os_unfair_lock是处于休眠状态

atomic原子锁自带一把自旋锁,只能保证setter、getter时的线程安全,在日常开发中使用更多的还是nonatomic修饰属性

atomic:当属性在调用setter、getter方法时,会加上自旋锁osspinlock,用于保证同一时刻只能有一个线程调用属性的读或写,避免了属性读写不同步的问题。由于是底层编译器自动生成的互斥锁代码,会导致效率相对较低

nonatomic:当属性在调用setter、getter方法时,不会加上自旋锁,即线程不安全。由于编译器不会自动生成互斥锁代码,可以提高效率

@synchronized在底层维护了一个哈希表进行线程data的存储,通过链表表示可重入(即嵌套)的特性,虽然性能较低,但由于简单好用,使用频率很高

NSLock、NSRecursiveLock底层是对pthread_mutex的封装

NSCondition和NSConditionLock是条件锁,底层都是对pthread_mutex的封装,当满足某一个条件时才能进行操作,和信号量dispatch_semaphore类似

锁的使用场景

如果只是简单的使用,例如涉及线程安全,使用NSLock即可

如果是循环嵌套,推荐使用@synchronized,主要是因为使用递归锁的 性能 不如 使用@synchronized的性能(因为在synchronized中无论怎么重入,都没有关系,而NSRecursiveLock可能会出现崩溃现象)

在循环嵌套中,如果对递归锁掌握的很好,则建议使用递归锁,因为性能好

如果是循环嵌套,并且还有多线程影响时,例如有等待、死锁现象时,建议使用@synchronized

你可能感兴趣的:(锁的原理随笔)