说说Java的AbstractQueuedSynchronizer
Java中的Lock锁,都是基于AQS,也就是AbstractQueuedSynchronizer。那么AbstractQueuedSynchronizer又是基于什么原理来做锁的呢?
一:锁原理
1. 怎么算获取到了锁?
AQS内置了一个volatile int state字段,利用CAS保证并发安全,当state=0表示没有线程占用。只要线程设置了state(比如置为1,如果
是可重入锁,state可以>1),就表示获取了锁。
2. 线程竞争时如何保存竞争的线程?
AQS内置了一个双链表,当线程存在竞争时,会先放入这个双链表的尾部。当然,这个放入的动作,也要保证并发安全,这里也是使用了
CAS原子操作,死循环执行这个放入动作直至成功。
3. 竞争线程保存后如何获取锁?
AQS做的很简单,直接死循环尝试获取锁直至成功。具体思路为:
3.1 取当前线程所属节点的上一个节点A。
3.2 如果节点A为头结点,则当前线程尝试获取锁。
3.3 没有获取到锁,则循环3.1 、 3.2
3.4 当获取到锁后,将当前节点置为头结点。这样,排在后边的节点,就会开始获取锁。
之所以要先判断上一个节点为头结点才尝试获取锁,主要有两个原因:
一:CAS操作相对于单纯的判断更加吃CPU。
二:这样保证了获取锁的公平性,方便公平锁的实现。
4. 锁释放
各种不同类型的锁,锁释放思路不同。但是都要刷 state=state-1。
二:AQS的优缺点
优点:可以实现语义更加丰富的锁,比如读写锁、公平锁、非公平锁,这是Java内置的synchronized所做不到的。
缺点:当线程竞争并发度极高时,会非常吃CPU,对系统性能有影响。当然在这种场景下,synchronized也不会更有优势,原因是synchronized锁升级到重量锁时,会利用操作系统的mutex命令,虽然这个命令并不吃CPU,但是频繁的用户态和内核态的上下文切换,也是很吃CPU的。
浙公网安备 33010602011771号