我最近遇到的最困难的调试问题是异步操作之间的死锁。例如,给定两个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);
-
Use CompletionStage.handle()
如果阶段按预期完成,则禁用 deadlockListener:
return stage.handle((value, throwable) ->
{
// WARNING: By design, CompletionStage.whenComplete() suppresses any exceptions thrown by its argument, so we use handle() instead.
deadlockListener.cancel(false);
if (throwable == null)
return value;
return rethrowException(throwable);
});
-
为了完整起见,您还拥有:
/**
* 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);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)