Home > database >  How to throw an exception on another Thread in Java?
How to throw an exception on another Thread in Java?

Time:11-18

So I'm using ListenableFuture as a return type for certain operations. I expect the users to add callback to the future and then handle the success and exception cases. Now if the user cannot handle the exception, I want to have the ability to throw that exception onto the main Thread. Here's some code example:

public class SomeProcessor {

  ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());

  public ListenableFuture<String> doStringProcessing() {
    return executor.submit(() -> doWork());
  }

  private String doWork() {
    return "stuff";
  }

}

Then in a client class:

public class SomeConsumer {

  public SomeConsumer (SomeProcessor processor) {
    Futures.addCallback(processor.doStringProcessing(), new FutureCallback<String>() {
      @Override
      public void onSuccess(String result) {
        // do something with result
      }

      @Override
      public void onFailure(Throwable t) {
        if (t instanceof ExceptionICanHandle) {
          // great, deal with it
        } else {
          // HERE I want to throw on the Main thread, not on the executor's thread
          // Assume somehow I can get a hold of the main thread object
          mainThread.getUncaughtExceptionHandler().uncaughtException(mainThread, t);
          // This above code seems wrong???
          throw new RuntimeException("Won't work as this is not on the mainthread");
        }
      }
    }, MoreExecutors.directionExecutor());
  }

}

CodePudding user response:

There is no direct way to do this.1

Hence, this question boils down to a combination of 2 simple things:

  • How do I communicate some data from a submitted task back to the code that is managing the pool itself? Which boils down to: How do I send data from one thread to another, and...
  • How do I throw an exception - which is trivial - throw x;.

In other words, you make the exception in your task, and do not throw it, instead, you store the object in a place the main thread can see it, and notify the main thread they need to go fetch it and throw it. Your main thread waits for this notification and upon receiving it, fetches it, and throws it.

A submitted task cannot simply 'ask' for its pool or the thread that manages it. However, that is easy enough to solve: Simply pass either the 'main thread' itself, or more likely some third object that serves as common communication line between them, to the task itself, so that task knows where to go.

Here is one simplistic approach based on the raw synchronization primitives baked into java itself:

public static void main(String[] args) {
  // I am the main thread
  // Fire up the executorservice here and submit tasks to it.
  // then ordinarily you would let this thread end or sleep.
  // instead...

  ExecutorService service = ...;
  AtomicReference<Throwable> err = new AtomicReference<>();
  Runnable task = () -> doWork(err);
  service.submit(task);
  while (true) {
    synchronized (err) {
      Throwable t = err.get();
      if (t != null) throw t;
      err.wait();
    }
  }
}

public void doWork(AtomicReference<Throwable> envelope) {
  try {
    doActualWork();
  catch (Throwable t) {
    synchronized (envelope) {
      envelope.set(t);
      envelope.notifyAll();
    }
  }
}

There are many, many ways to send messages from one thread to another and the above is a rather finicky, primitive form. It'll do fine if you don't currently have any comms channels already available to you. But, if you already have e.g. a message queue service or the like you should probably use that instead here.

[1] Thread.stop(someThrowable) literally does this as per its own documentation. However, it doesn't work - it's not just deprecated, it has been axed entirely; calling it throws an UnsupportedOperationException on modern VMs (I think at this point 10 years worth of releases at least), and is marked deprecated with the rather ominous warning of This method is inherently unsafe. and a lot more to boot, it's not the right answer.

  • Related