其实关于这一点,网上大部分人都会说,要 await()必然要先 lock(),既然 lock()了就表示没有竞争,没有竞争自然也没必要使用 volatile+CAS 的机制去保证什么。所以 firstWaiter 不需要是 volatile 的。
但是实际上我感觉还是会有问题,比如你执行下面这段程序:
final Lock lock=new ReentrantLock();
Condition Emptycondition = lock.newCondition();
Emptycondition.await(); //这样会抛出异常
比如你让一个子线程去做上面的事,会抛出异常,因为 await()的时候没有获取锁呢。但是在抛出异常之前,你已经执行了一部分代码了:
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);//在这里抛出的异常,意味着已经执行了 addConditionWaiter
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();
if (release(savedState)) {//具体的说,是这里抛出异常了
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)//抛出异常后,failed 为真,但善后处理只是将 node 的状态变成 CANCELLED
node.waitStatus = Node.CANCELLED;
}
}
再来看 addConditionWaiter 的逻辑:
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;//对不保证可见性的变量进行赋值了
else
t.nextWaiter = node;
lastWaiter = node;//对不保证可见性的变量进行赋值了
return node;
}
好了,现在执行完我的那三行测试代码后,Emptycondition 的条件队列上有了一个 node,而且这个 node 包装了一个死掉的线程。因为抛出异常
现在假设有另一个线程执行正常流程的代码:
final Lock lock=new ReentrantLock();
Condition Emptycondition = lock.newCondition();
lock.lock();
try{
Emptycondition.await();
} finally {
lock.unlock();
}
现在条件是:
1
sagaxu 2020-06-10 00:19:53 +08:00 via Android
因为你的代码没有做到要 await()必然要先 lock()
|
2
amiwrong123 OP @sagaxu
额,我好像没懂你意思。所以你想说 firstWaiter 不加 volatile,就会有问题呗 |
3
muyunn 2020-06-10 01:10:17 +08:00 via iPhone
好长时间不用,差点被楼主绕进去,你必须先调用 lock.lock()获取锁,然后才可以 await
|
4
vk42 2020-06-10 01:16:50 +08:00
lz 是想说不先 lock 的情况下后果不可控?但本来没按规则先 lock 不就是 bug 吗?直接修 bug 不是更简单么
|
5
luckyrayyy 2020-06-10 02:41:50 +08:00 via iPad
我的哥啊,你要研究一年 aqs ?
|
6
amiwrong123 OP @muyunn
是的,标准用法是这样 |
7
amiwrong123 OP @vk42
好吧,所以相当于决定权给了用户,作者默认使用者是按照正确用法使用的 |
8
amiwrong123 OP @luckyrayyy
哈哈哈,最近发帖太多被发现了吗。这几天 condition 的实现看完,就不看 aqs 了 |
9
yamasa 2020-06-10 15:37:10 +08:00
佩服这牛角尖精神。虽然我觉得现行环境下死磕 jdk 源码不如刷 leetcode 来的现实。
|
10
amiwrong123 OP @yamasa
哎,所以我是又努力错方向了吗 == |
11
lux182 2020-06-10 20:35:59 +08:00
我觉得如果你用最简洁的语言表述清楚了,可能你也就找到答案了
|
12
amiwrong123 OP 给自己回复一下,我问的这个问题,其实是个 bug,详见 https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8187408
|