为啥 AQS 的 CondtionObject 的 firstWaiter 不需要是 volatile 的?
資深大佬 : amiwrong123 8
其实关于这一点,网上大部分人都会说,要 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(); }
现在条件是:
- 线程 A 执行 测试代码,会执行到 addConditionWaiter
- 线程 B 执行 正常流程代码,也会执行到 addConditionWaiter
- 两个线程可能同时执行 addConditionWaiter,却没有对 firstWaiter 进行 volatile+CAS 的保护,难道这样不会有问题吗?
大佬有話說 (11)