从自旋锁 到 CLH 到 AQS
这篇主要讲讲, 为啥 会有 CLH,以及为啥会有 AQS,他们主要做了啥。
CAS
首先说下 CAS,之后的锁都以此为基础, cpu保证了他的 原子性
自旋锁
多个线程忙等待锁释放,由于不牵涉到信号量,不依赖操作系统的调度,在同一个上下文内,所以获取和释放锁的开销很小。
但是等待锁期间会占用大量cpu。
简单例子
1 | public class SpinLock { |
公平自旋锁
但是自旋锁有个问题,当有多个自旋锁存在时,是抢占式的,无法控制优先级,会造成线程饥饿
为了解决公平性问题,于是就想出了维护一个自旋锁链,实现上有几个代表
TicketLock, CLH, MCS
TicketLock
每一个想要获取锁的线程都分配一个 自增的 ticket,各个自旋锁自旋公共的ticket
缺点,多线程并发访问公共字段,为了保证可见性,每次读写操作都必须在多个处理器缓存之间进行缓存同步, 会降低系统整体性能
CLH
将锁维护在一个链表上,每个锁自旋本线程的本地变量(该变量为前驱状态),当自己的前驱unlock后,将会修改后置链表节点的前驱状态,以便后面线程获取到锁。
线程切换仅发生在 unlock 的操作时
MCS
同 CLH 锁, 自旋本地变量,本地变量的状态再前置线程unlock时修改。
线程切换仅发生在 unlock 的操作时
总结:
CLH 的实现比 MCS 更简单。
CLH 和 MCS 的代码可以参考这里
https://coderbee.net/index.php/concurrent/20131115/577
AQS
AQS是基于CLH 修改的.
公平性靠CLH链实现,但是将自旋锁 替换成了 同步阻塞器