并发:Condition.awaitNanos()未释放锁

3

在我的程序中,我正在使用从一个对象创建的Condition对象。

private static final Lock lock = new ReentrantLock();

就像这样:

private static final Condition operationFinished = MyClass.lock.newCondition();

偶尔(并发问题总是会出现这种情况)我会遇到以下行为:
  1. Thread1获取锁
  2. Thread1调用operationFinished.awaitNanos() - 这应该挂起Thread1并释放锁。
  3. Thread2尝试获取相同的锁,但调试输出显示Thread1仍然持有锁!
根据文档,这种行为是不可能的,因为在awaitNanos()上,Thread1首先释放锁,然后挂起。如果它没有释放锁,那么它就不会挂起,因此Thread2甚至无法获得获取锁的机会。
有人经历过类似的情况吗?这个错误只会在100次中发生一次 - 但仍表明我要么没有正确使用并发工具,要么java.utils.concurrent.*包中存在某种错误(我怀疑)。
更新:
作为对Peter答案的回应:
我观察到以下行为:显然,两个线程互相死锁。我可以看到Thread2被阻塞(等待锁),同时Thread1中的awaitNanos()从未超时。
2个回答

1

根据您查看此信息的方式,我已经看到了许多例子,其中多个线程在一个对象上等待(wait()),但仍然说他们都持有相同的锁。这可能是由于堆栈跟踪或监视不准确。

假设您有一个线程1正在持有锁,但在awaitNanos()中,您有线程2正在尝试获取锁(),但有时线程3也在持有锁....

我会执行jstack -l {pid}来检查所有可能持有锁的线程。

如果锁死了,awaitLock(也不是wait())将不会返回,因为它必须在这样做之前获取锁。(除非被中断)


谢谢您的回复!我已经更新了我的答案,并提供了更多信息:锁定明显仍由Thread1持有,因为我可以看到Thread2在lock()上阻塞。 - quaylar
Thread1 确实在 awaitNanos() 中吗?您能展示这两个线程以及可能持有锁的任何其他线程的堆栈跟踪吗?例如,使用 jstack - Peter Lawrey
我意识到当Thread1还没有进入awaitNanos()时,Thread2可能会尝试获取锁,但即使在这种情况下,Thread2也会被阻塞,直到Thread1调用awaitNanos()并获得锁。 - quaylar
1
下次出现这个错误时,我将发布jstack的输出结果,不幸的是它并不容易重现 :( - quaylar

1

你确定等待时间还没有结束吗?如果你只等待了短暂的一段时间(例如几百纳秒),那么在Thread2完全启动之前,等待时间可能已经过期,这种情况下Thread1可能会先被重新激活。


如果等待时间过期,我会抛出一个异常,但是当这个问题发生时,没有异常,也没有更多的调试输出(在Thread1在await()之后继续执行的情况下仍应该输出)。 - quaylar
你在抛出异常之前释放了锁吗?请记住,如果抛出异常的线程不是主线程,则可能会在该线程上冒泡调用堆栈,然后消失,除非您为该线程设置了未捕获的异常处理程序。 - Sean Reilly
问题在于,在当前情况下,异常从未被抛出,因此awaitNanos()永远不会返回...似乎Thread1和Thread2互相死锁了。 - quaylar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接