看一下ReteenLock中的tryRelease实现
就是减一下资源值 。
当资源值清零,则说明可以解除了对当前点的占用
protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) {free = true; // 设置当前占用线程为null setExclusiveOwnerThread(null); } // 不需要CAS , 因为只有持有锁的人才能做释放,不担心竞争 setState(c); return free; }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的,因为不会竞争式地重入!
ReentrantLock释放时,也会处理重入 , 关键点就是对getState() - release后的处理,是否返回true或者false
protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { // 只有资源数为0才会解锁 // 才算释放成功,否则这锁还是占住了free = true; setExclusiveOwnerThread(null); } setState(c); return free;}AQS如何响应超时AQS提供的方法中带有Nanos后缀的方法就是支持超时中断的方法 。
核心逻辑就是每次阻塞前 , 确认nanosTimeout是否已经超时了 。
推荐阅读
- Briefings in Bioinformatics-2021 知识图谱-生物信息学-医学顶刊论文:生物信息学中的图表示学习:趋势、方法和应用
- golang中的nil接收器
- golang中的字符串
- DevOps|从特拉斯辞职风波到研发效能中的不靠谱人干的荒唐事
- 月圆之夜镜中的记忆全成就攻略是什么
- 怎样转发微信内容到朋友圈(如何将微信中的内容转发到朋友圈)
- 4 Java I/O:AIO和NIO中的Selector
- 时空中的绘旅人诸界归一出行服装羽翼获取方法是什么
- 时空中的绘旅人服装的获取方法是什么
- 划拳中的十五、二十怎么玩啊(划拳必赢的十大技巧)