锁的原理随笔

简介: synchronized, NSLock, 递归锁, 条件锁

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 -> 链表

  1. 链表查询, 缓存查找以及递归 -> 耗内存 -> 耗性能 -> 性能低
  2. 方便简单, 不用解锁,使用频率高
  3. 不能对非OC对象加锁
  4. @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




目录
相关文章
|
7月前
|
存储 人工智能 关系型数据库
10个行锁、死锁案例⭐️24张加锁分析图🚀彻底搞懂Innodb行锁加锁规则!
10个行锁、死锁案例⭐️24张加锁分析图🚀彻底搞懂Innodb行锁加锁规则!
|
4月前
|
Java
【多线程面试题二十五】、说说你对AQS的理解
这篇文章阐述了对Java中的AbstractQueuedSynchronizer(AQS)的理解,AQS是一个用于构建锁和其他同步组件的框架,它通过维护同步状态和FIFO等待队列,以及线程的阻塞与唤醒机制,来实现同步器的高效管理,并且可以通过实现特定的方法来自定义同步组件的行为。
【多线程面试题二十五】、说说你对AQS的理解
|
4月前
|
Java
【多线程面试题十六】、谈谈ReentrantLock的实现原理
这篇文章解释了`ReentrantLock`的实现原理,它基于Java中的`AbstractQueuedSynchronizer`(AQS)构建,通过重写AQS的`tryAcquire`和`tryRelease`方法来实现锁的获取与释放,并详细描述了AQS内部的同步队列和条件队列以及独占模式的工作原理。
【多线程面试题十六】、谈谈ReentrantLock的实现原理
|
4月前
|
Java
【多线程面试题二十二】、 说说你对读写锁的了解
这篇文章讨论了读写锁(ReadWriteLock)的概念和应用场景,强调了读写锁适用于读操作远多于写操作的情况,并介绍了Java中`ReentrantReadWriteLock`实现的读写锁特性,包括公平性选择、可重入和可降级。
|
4月前
|
Java 调度
【多线程面试题十四】、说一说synchronized的底层实现原理
这篇文章解释了Java中的`synchronized`关键字的底层实现原理,包括它在代码块和方法同步中的实现方式,以及通过`monitorenter`和`monitorexit`指令以及`ACC_SYNCHRONIZED`访问标志来控制线程同步和锁的获取与释放。
|
存储 Java
【一文读懂】 Java并发 - 锁升级原理
Java对象头,锁升级的原因,重量级锁、轻量级锁、偏向锁的原理
392 0
【一文读懂】 Java并发 - 锁升级原理
|
机器学习/深度学习 算法 安全
二十二、死锁
二十二、死锁
二十二、死锁
|
机器学习/深度学习 安全 Java
java并发原理实战(10)--AQS 和公平锁分析
java并发原理实战(10)--AQS 和公平锁分析
135 0
java并发原理实战(10)--AQS 和公平锁分析
|
设计模式 算法 Java
java并发原理实战(8)-- lock接口使用和认识
java并发原理实战(8)-- lock接口使用和认识
118 0
java并发原理实战(8)-- lock接口使用和认识