AQS的智慧:短暂自旋 + 深度阻塞
面试的时候被问到AQS怎么实现高性能?很多人可能一上来就背教科书,什么CLH队列、state变量。但AQS真正有意思的地方,在于它把"乐观"和"悲观"两种思路玩明白了。
先试试,不行再排队
AQS核心就两个东西:一个volatile的state变量表示状态,一个CLH双向队列管等待的线程。
线程来拿锁的时候,第一反应不是"我要等着",而是"我试试能不能直接拿到"。直接用CAS去改state,改成功就完事了,连阻塞都不用,全程无开销。
如果CAS失败了,说明有人在抢,这时候才去排队。
排队也讲究策略
入队也不是傻等着,AQS用的是无锁入队——用CAS把节点插到队列尾部。插队之后,线程会做一个自旋判断:如果自己是队列里的老二,就再tryAcquire一次。
为什么给第二次机会?赌的就是前一个线程刚好释放锁。概率虽小,但一旦赌中,成本极低,比直接park省了一次上下文切换。
自旋几次还是拿不到怎么办?调用LockSupport.park()把自己挂起。等前一个节点释放锁时把它唤醒。
总结一下
AQS这套组合拳打下来:
- CAS乐观尝试——能不动用阻塞就不动
- 无锁入队——快速进入排队系统
- 短暂自旋——给一次捡漏机会
- 最终park——竞争激烈时切到零消耗模式
说白了就是一句话:先试试,失败了再等,别一上来就把线程搞休眠。成本从低到高,层层递进,就是AQS性能还行的秘密。
