Home > front end >  How to detect an exception thrown in another thread?
How to detect an exception thrown in another thread?

Time:09-27

I would like to know if a code in a thread does throw an exception. When I run the following test :

@Test
void should_throw_IllegalArgumentException() {
    assertThatExceptionOfType(IllegalArgumentException.class)
        .isThrownBy(() -> {
            new Thread(() -> new IllegalArgumentException()).start();
        });
}

Then the result is the following:

java.lang.AssertionError: 
Expecting code to raise a throwable.

Do you have an explanation? Do you know how to detect the IllegalArgumentException in this thread?

CodePudding user response:

I suppose you forgot the throw keyword.

throw new IllegalArgumentException()

but this wouldn't help since you are in another thread. I've got this passed:

  @Test
  void should_throw_IllegalArgumentException() throws InterruptedException {
    Thread.UncaughtExceptionHandler h = (th, ex) -> assertTrue(ex instanceof IllegalArgumentException);

    Thread t = new Thread(() -> {
      throw new IllegalArgumentException();
    });

    t.setUncaughtExceptionHandler(h);
    t.start();
    t.join();
  }

or use Thread.setDefaultUncaughtExceptionHandler() and set default handler for all threads on each test.

CodePudding user response:

The only way that this can throw an exception is if there is some reason why the new thread cannot be created:

    new Thread(...).start();

Whatever exception happens in the new thread stays in the new thread. If you want to know whether an exception happened in the new thread, then you'll have to write code to catch it in the new thread, and then the handler can pass the exception object to other threads in any of the same ways that you would use to pass any other object from one thread to another.

The CompletableFuture class offers one way.*

CompletableFuture<Foobar> cf = new CompletableFuture<>();
new Thread(() -> {
    try {
        cf.complete(makeMeAFoobar(...));
    }
    catch (Throwable t) {
        cf.completeExceptionally(t);
    }
}).start

... do some other stuff, and then...

// This either will return the `Foobar` object that was returned by the
// `makeMeAFoobar()` call in the other thread, or else it will throw
// an `ExecutionException` containing an exception that was caught in 
// the other thread.
//
Foobar foobar = cf.get();

* Maybe somebody can suggest a more elegant way to use CompletableFuture in an explicitly created new Thread(...). But, if you're willing to use the default thread pool instead of explicitly creating a thread, then you could save a few lines of code by writing this instead:

CompletableFuture<Foobar> cf = new CompletableFuture.runAsync(() -> {
    return makeMeAFoobar(...);
});

... do some other stuff ...

// This either will return the `Foobar` object...or else it will throw...
Foobar foobar = cf.get();
  • Related