简体   繁体   中英

Why doesn't method appear twice in async nested Exception stack trace?

I'm using the sample code below in which a CompletableFuture is chained multiple times, and I'm wondering why the chaining code doesn't appear multiple times in the stack trace when an exception is thrown.

package com.yannick;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;

public class AsyncExceptionNestingTestMain {
    public static void main(String[] args) throws Exception {
        CompletableFuture<String> failingCF = createFailingCF();
        CompletableFuture<String> chained = chainAndNestException(failingCF);
        CompletableFuture<String> chained2 = chainAndNestException(chained);
        chained2.get();
    }

    private static CompletableFuture<String> chainAndNestException(CompletableFuture<String> toChain) {
        return toChain.handleAsync((result, exception) -> {
            if (exception != null)
                throw new CompletionException(exception);
            return result;
        });
    }

    private static CompletableFuture<String> createFailingCF() {
        return CompletableFuture.supplyAsync(() -> {
            throw new RuntimeException("Failed!");
        });
    }
}

The exception is the following:

Exception in thread "main" java.util.concurrent.ExecutionException: java.util.concurrent.CompletionException: java.util.concurrent.CompletionException: java.lang.RuntimeException: Failed!
    at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
    at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)
    at com.yannick.AsyncExceptionNestingTestMain.main(AsyncExceptionNestingTestMain.java:11)
Caused by: java.util.concurrent.CompletionException: java.util.concurrent.CompletionException: java.lang.RuntimeException: Failed!
// here is the only occurence of the chaining method:
    at com.yannick.AsyncExceptionNestingTestMain.lambda$chainAndNestException$0(AsyncExceptionNestingTestMain.java:17)
    at java.util.concurrent.CompletableFuture.uniHandle(CompletableFuture.java:822)
    at java.util.concurrent.CompletableFuture$UniHandle.tryFire(CompletableFuture.java:797)
    at java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:443)
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: java.util.concurrent.CompletionException: java.lang.RuntimeException: Failed!
    at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273)
    at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280)
    at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1592)
    at java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1582)
    ... 4 more
Caused by: java.lang.RuntimeException: Failed!
    at com.yannick.AsyncExceptionNestingTestMain.lambda$createFailingCF$1(AsyncExceptionNestingTestMain.java:24)
    at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
    ... 5 more

The CompletionException is handled as a special type of Exception by the CompletableFuture in reportGet:

        if ((x instanceof CompletionException) && (cause = x.getCause()) != null)
            x = cause;

So when you throw a CompletionException in your code, only it's cause is reported. You can see the chained exceptions if you replace:

    throw new CompletionException(exception);

by (for instance):

    throw new RuntimeException(exception);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM