鉴于这个问题将近一个月没有收到任何答案,我将发布迄今为止找到的最佳解决方案:
DebugCompletableFuture.java :
public final class DebugCompletableFuture<T> extends CompletableFuture<T>{ private static RunMode RUN_MODE = RunMode.DEBUG; private static final Set<String> CLASS_PREFIXES_TO_REMOVE = ImmutableSet.of(DebugCompletableFuture.class.getName(), CompletableFuture.class.getName(), ThreadPoolExecutor.class.getName()); private static final Set<Class<? extends Throwable>> EXCEPTIONS_TO_UNWRAP = ImmutableSet.of(AsynchronousException.class, CompletionException.class, ExecutionException.class); private final CompletableFuture<T> delegate; private final AsynchronousException asyncStacktrace; private DebugCompletableFuture(CompletableFuture<T> delegate) { requireThat("delegate", delegate).isNotNull(); this.delegate = delegate; this.asyncStacktrace = new AsynchronousException(); delegate.whenComplete((value, exception) -> { if (exception == null) { super.complete(value); return; } exception = Exceptions.unwrap(exception, EXCEPTIONS_TO_UNWRAP); asyncStacktrace.initCause(exception); filterStacktrace(asyncStacktrace, element -> { String className = element.getClassName(); for (String prefix : CLASS_PREFIXES_TO_REMOVE) if (className.startsWith(prefix)) return true; return false; }); Set<String> newMethods = getMethodsInStacktrace(asyncStacktrace); if (!newMethods.isEmpty()) { Set<String> oldMethods = getMethodsInStacktrace(exception); newMethods.removeAll(oldMethods); if (!newMethods.isEmpty()) { // The async stacktrace introduces something new super.completeExceptionally(asyncStacktrace); return; } } super.completeExceptionally(exception); }); } private Set<String> getMethodsInStacktrace(Throwable exception) { requireThat("exception", exception).isNotNull(); Set<String> result = new HashSet<>(); for (StackTraceElement element : exception.getStackTrace()) result.add(element.getClassName() + "." + element.getMethodName()); for (Throwable suppressed : exception.getSuppressed()) result.addAll(getMethodsInStacktrace(suppressed)); return result; } public static <T2> CompletableFuture<T2> wrap(CompletableFuture<T2> delegate) { if (RUN_MODE != RunMode.DEBUG) return delegate; return new DebugCompletableFuture<>(delegate); } private void filterStacktrace(Throwable exception, Predicate<StackTraceElement> elementFilter) { Throwable cause = exception.getCause(); if (cause != null) filterStacktrace(cause, elementFilter); for (Throwable suppressed : exception.getSuppressed()) filterStacktrace(suppressed, elementFilter); StackTraceElement[] elements = exception.getStackTrace(); List<StackTraceElement> keep = new ArrayList<>(elements.length); for (StackTraceElement element : elements) { if (!elementFilter.test(element)) keep.add(element); } exception.setStackTrace(keep.toArray(new StackTraceElement[0])); } @Override public <U> CompletableFuture<U> thenApply(Function<? super T, ? extends U> fn) { return wrap(super.thenApply(fn)); } @Override public <U> CompletableFuture<U> thenApplyAsync(Function<? super T, ? extends U> fn) { return wrap(super.thenApplyAsync(fn)); } @Override public <U> CompletableFuture<U> thenApplyAsync(Function<? super T, ? extends U> fn, Executor executor) { return wrap(super.thenApplyAsync(fn, executor)); } @Override public CompletableFuture<Void> thenAccept(Consumer<? super T> action) { return wrap(super.thenAccept(action)); } @Override public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) { return wrap(super.thenAcceptAsync(action)); } @Override public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor) { return wrap(super.thenAcceptAsync(action, executor)); } @Override public CompletableFuture<Void> thenRun(Runnable action) { return wrap(super.thenRun(action)); } @Override public CompletableFuture<Void> thenRunAsync(Runnable action) { return wrap(super.thenRunAsync(action)); } @Override public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor) { return wrap(super.thenRunAsync(action, executor)); } @Override public <U, V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T, ? super U, ? extends V> fn) { return wrap(super.thenCombine(other, fn)); } @Override public <U, V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T, ? super U, ? extends V> fn) { return wrap(super.thenCombineAsync(other, fn)); } @Override public <U, V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T, ? super U, ? extends V> fn, Executor executor) { return wrap(super.thenCombineAsync(other, fn, executor)); } @Override public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action) { return wrap(super.thenAcceptBoth(other, action)); } @Override public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action) { return wrap(super.thenAcceptBothAsync(other, action)); } @Override public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action, Executor executor) { return wrap(super.thenAcceptBothAsync(other, action, executor)); } @Override public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action) { return wrap(super.runAfterBoth(other, action)); } @Override public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action) { return wrap(super.runAfterBothAsync(other, action)); } @Override public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action, Executor executor) { return wrap(super.runAfterBothAsync(other, action, executor)); } @Override public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn) { return wrap(super.applyToEither(other, fn)); } @Override public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn) { return wrap(super.applyToEitherAsync(other, fn)); } @Override public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn,Executor executor) { return wrap(super.applyToEitherAsync(other, fn, executor)); } @Override public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action) { return wrap(super.acceptEither(other, action)); } @Override public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action) { return wrap(super.acceptEitherAsync(other, action)); } @Override public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor) { return wrap(super.acceptEitherAsync(other, action, executor)); } @Override public CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action) { return wrap(super.runAfterEither(other, action)); } @Override public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action) { return wrap(super.runAfterEitherAsync(other, action)); } @Override public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action, Executor executor) { return wrap(super.runAfterEitherAsync(other, action, executor)); } @Override public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) { return wrap(super.thenCompose(fn)); } @Override public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) { return wrap(super.thenComposeAsync(fn)); } @Override public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor) { return wrap(super.thenComposeAsync(fn, executor)); } @Override public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn) { return wrap(super.exceptionally(fn)); } @Override public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action) { return wrap(super.whenComplete(action)); } @Override public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action) { return wrap(super.whenCompleteAsync(action)); } @Override public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor) { return wrap(super.whenCompleteAsync(action, executor)); } @Override public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn) { return wrap(super.handle(fn)); } @Override public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn) { return wrap(super.handleAsync(fn)); } @Override public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) { return wrap(super.handleAsync(fn, executor)); } @Override public boolean complete(T value) { return delegate.complete(value); } @Override public boolean completeExceptionally(Throwable ex) { return delegate.completeExceptionally(ex); }}
RunMode.java :
public enum RunMode{ DEBUG, RELEASE}
AsynchronousException.java
public final class AsynchronousException extends RuntimeException{ private static final long serialVersionUID = 0L; public AsynchronousException() { }}
用法:
DebugCompletableFuture.wrap(CompletableFuture.supplyAsync(this::expensiveOperation));
好处:您将获得相对干净的异步堆栈跟踪。
缺点:
AsynchronousException每创造一个未来就建造一个新产品是非常昂贵的。具体来说,如果您生成大量的期货,这会在堆上生成大量垃圾,并且GC开销会变得明显。
我仍然希望有人会提出更好的方法。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)