重量级锁的8连问

文章详细对比了重量级锁的ObjectMonitor与JUC的AQS在实现上的异同,解释了为何ObjectMonitor需要两个等待队列以及线程唤醒和入队的顺序。同时,讨论了synchronized的公平锁/非公平锁逻辑。

1. 重量级锁的ObjectMonitor和JUC中的AQS有什么异同?

都是通过共享变量和等待队列实现的。
共享变量
ObjectMonitor通过共享变量owner(CAS将owner从null设置成为当前线程来抢锁),同时需要额外字段记录锁的重入次数。
AQS是通过共享变量state(CAS将state从0设置成1抢锁),同时需要额外字段exclusiveOwnerThread来记录当前持有锁的线程;
队列
ObjectMonitor等待队列使用了两个队列,cxq和entryList。
AQS需要一个队列CLH(先进先出)。
唤醒
ObjectMonitor可以实现精准唤醒;AQS不支持。
精准唤醒demo

2. 为什么ObjectMonitor需要cxq和entryList两个等待队列

ObjectMonitor中加解锁、wait/notify都涉及对等待队列的进出队操作。如果使用一个队列冲突的概率会加大,耗费系统资源。分成2个队列后,进出队EntryList队列只有加锁的情况才会操作,不需要CAS和自旋,减少了资源消耗。

3. cxq队列中等待线程,什么时候会进到EntryList

抢锁线程在获取锁失败后,默认会进cxq队列。当持有锁的线程执行完释放锁时,会将cxq中的等待节点放入EntryList中。就是说cxq->EntryList这一步是锁释放之前的由持有锁的线程做的。

4. 等待队列中多个线程,唤醒的顺序是什么

当持有锁的线程释放锁时,会先检查EntryList是否为空,如果不为空则唤醒EntryList中第一个节点。否则唤醒cxq中第一个节点。

5. 偏向锁和轻量级锁下线程是否可以wait和notify

6. cxq和waitset数据结构有什么区别

cxq是一个单向链表,采用先进后出的策略,就是说后入队的线程将先获取到互斥锁。
waiset是一个回环链表,即尾节点的下一个节点是头节点,采用先进先出的策略。

7. 被唤醒的wait线程和其它等待线程,谁会先抢到锁

使用notify和notifyAll唤醒wait线程,jvm的处理是有区别的。
1)如果是notify,唤醒的是waitset的队首节点,如果这时候EntryList不为空,则放入EntryList,否则放入cxq。无论是放入那个队列。因为是cxq后进先出,所以被唤醒的线程比等待队列中的线程先出队,会先抢到锁。
2)如果是notifyAll,会将waitset中的所有节点逐个放入cxq中。按照问题4中的描述,如果EntryList不为空,则EntryList中首节点会先抢到锁,否则waitset中原最后一个节点先抢到锁,如下图所示:
在这里插入图片描述

8. synchronized有类似AQS的公平锁/非公平锁逻辑吗

默认情况下,线程进入重量级锁的抢锁阶段,第一步就会尝试通过自旋来抢锁,所以默认相当于AQS中的非公平锁。即使自旋时未抢到锁,按照上面讲的cxq出入队逻辑,也是后进先出,正常情况下后进入等待队列的线程会先抢到锁,这一点也是和AQS中相反的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值