AQS如何实现公平和非公平?以ReteenLock为例 , 它内部tryAcquire有两种同步器的实现
- 非公平同步器NonfairSync
- 公平同步器FairSync
ReentrantLock根据配置的不同 , 使用这2个同步器做资源的获取和同步操作
他们二者的提供的lock操作 , 本质上就是AQS的acquire(1)
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); }二者在公平和非公平的实现区别上 , 就是唤醒线程后 , 只有等待队列的队头节点才会尝试竞争 。
而非公平锁是只要唤醒了就可以尝试竞争 。
因此核心区别在于hasQueuedPredecessors方法!

文章插图
公平和非公平锁的优点和缺点
- 饥饿问题
- 性能问题
性能测试中公平锁的耗时是非公平锁的94.3倍 , 总切换次数是133倍
Lock类是默认公平还是非公平?默认是非公平的 , 原因就是上文考虑的性能差距过大问题 , 因此公平锁只能用于特定对性能要求不高且饥饿发生概率不大的场景中 。
独占模式和共享模式的AQS区别
- 名字上 , 共享模式都会带一个shard
- 返回值上 , 独占模式相关acuire方法放回的是boolean类型 , 而共享模式返回的是int值
- 核心概念上 , 区别在于同一时刻能否有多个线程可以获取到其同步状态
- 释放时 , 共享模式需要用CAS进行释放 , 而独占模式的release方法则不需要 , 直接setState即可 。
- 共享模式应用:信号量、读写锁
和上面的RLock类一个区别在于需要state初始化值 , 不一定为1
Sync(int permits) { setState(permits); }再继承实现了FairSync和NoFairSync
使用CAS实现值的增加或者减少
公平/非公平的区别同样是hasQueuedPredecessors的判断
protected int tryAcquireShared(int acquires) { for (;;) { // 队头判断 , 公平锁核心 if (hasQueuedPredecessors()) return -1; int available = getState(); int remaining = available - acquires; // 信号量不足 , 直接返回负数 if (remaining < 0 || // 能抢成功 , 返回修改后的值 , 抢失败则for循环继续 compareAndSetState(available, remaining)) return remaining; } }AQS如何处理重入通过current == getExclusiveOwnerThread()来判断并进行非CAS的setState操作
if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; // 出现负数 , 说明溢出了 if (nextc < 0) // throw new Error("Maximum lock count exceeded"); // 因为是重入操作 , 可以直接进行state的增加 , 所以不需要CAS setState(nextc); return true;}注意处理重入问题时 , 如果是独占锁 , 是可以直接setState而不需要CAS的 , 因为不会竞争式地重入!
经验总结扩展阅读
- 许运东婉萍是什么电视剧中的人物?
- 为什么女儿9岁了睡觉头上出汗
- 迎战高考的励志祝福文案
- 许舒贝是什么电视剧中的人物?
- 裘正宇是什么电视剧中的人物?
- 西厂雨化田是什么电影中的人物?
- Briefings in Bioinformatics-2021 知识图谱-生物信息学-医学顶刊论文:生物信息学中的图表示学习:趋势、方法和应用
- 苏简和黄凯迪是什么电视剧中的人物?
- 3步训练培养你心目中的小神童
- 艾莉是什么电视剧中的人物?