Java 如何调试CompletableStage死锁?
我最近遇到的最困难的调试问题是异步操作之间的死锁。例如,给定两个Java 如何调试CompletableStage死锁?,java,debugging,asynchronous,completable-future,Java,Debugging,Asynchronous,Completable Future,我最近遇到的最困难的调试问题是异步操作之间的死锁。例如,给定两个CompletionStage链,其中第一个链调用依赖于第二个链完成的方法,第二个链调用依赖于第一个链完成的方法。这在现实生活中并不明显,因为依赖关系往往是隐藏的,有时死锁涉及三方以上 问题的一部分在于无法找出CompletableStage在等待什么。这是因为操作引用的是CompletableStage,而不是相反 现在大多数调试器都提供某种程度的死锁检测,但这只适用于线程如何调试CompletableStage链之间的死锁?我最
CompletionStage
链,其中第一个链调用依赖于第二个链完成的方法,第二个链调用依赖于第一个链完成的方法。这在现实生活中并不明显,因为依赖关系往往是隐藏的,有时死锁涉及三方以上
问题的一部分在于无法找出CompletableStage
在等待什么。这是因为操作引用的是CompletableStage
,而不是相反
现在大多数调试器都提供某种程度的死锁检测,但这只适用于线程如何调试CompletableStage链之间的死锁?我最后做了以下工作:
- 在每个
链的末尾,安排一个将在超时后触发的事件:CompletionStage
Set<Object> knownDeadlocks = ConcurrentHashMap.newKeySet(); // ... Future<?> deadlockListener = scope.getScheduler().schedule(() -> { if (knownDeadlocks.add(Throwables.getStackTraceAsString(context))) log.warn("Possible deadlock", context); }, DEADLOCK_DURATION.toMillis(), TimeUnit.MILLISECONDS);
- 为完整起见,您还需要:
/** * Rethrows a {@code Throwable}, wrapping it in {@code CompletionException} if it isn't already wrapped. * * @param <T> the return type expected by the caller * @param throwable a Throwable * @return an undefined value (the method always throws an exception) * @throws CompletionException wraps {@code throwable} */ public <T> T rethrowException(Throwable throwable) { if (throwable instanceof CompletionException) throw (CompletionException) throwable; if (throwable == null) throwable = new NullPointerException("throwable may not be null"); // According to https://stackoverflow.com/a/49261367/14731 some methods do not wrap exceptions throw new CompletionException(throwable); }
/** *返回一个{@code Throwable},如果它还没有被包装,则将其包装在{@code CompletionException}中。 * *@param调用方所需的返回类型 *@param throwable一个throwable *@返回未定义的值(该方法始终引发异常) *@throws CompletionException包装{@code throwable} */ 公共T rethrowException(可丢弃可丢弃) { if(可丢弃的CompletionException实例) throw(CompletionException)可丢弃; if(throwable==null) throwable=新的NullPointerException(“throwable可能不是null”); //据https://stackoverflow.com/a/49261367/14731 有些方法不包装异常 抛出新的CompletionException(可丢弃); }
new
/complete()
手动管理CompletableFuture
时,以及链中的一个任务在子级上调用get()
/join()
时。第一种情况似乎很难检测(可能需要代码分析),第二种情况似乎有点做作。这是现实生活中经常发生的事情吗?似乎更可能出现涉及其他机制的死锁(同步的
、数据库事务等)。
/**
* Rethrows a {@code Throwable}, wrapping it in {@code CompletionException} if it isn't already wrapped.
*
* @param <T> the return type expected by the caller
* @param throwable a Throwable
* @return an undefined value (the method always throws an exception)
* @throws CompletionException wraps {@code throwable}
*/
public <T> T rethrowException(Throwable throwable)
{
if (throwable instanceof CompletionException)
throw (CompletionException) throwable;
if (throwable == null)
throwable = new NullPointerException("throwable may not be null");
// According to https://stackoverflow.com/a/49261367/14731 some methods do not wrap exceptions
throw new CompletionException(throwable);
}