28

In Java thread, the 'run' method cannot throw a 'checked exception'. I came across this in the Core Java (vol 1) book. Can someone please explain the reasoning behind it?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
Neel
  • 9,913
  • 16
  • 52
  • 74
  • 7
    Where would the exception go? The thread that spawned the new thread has since moved on. So, how would the new thread "send" the exception to the origination thread that had fired and forgotten said thread? – chaotic3quilibrium Dec 20 '10 at 16:36

6 Answers6

29

Can someone please explain the reasoning behind it?

Yes, because any exception you throw in run method will be carefully ignored by JVM. Thus, throwing it there is probably a mistake (unless you have specific exception handler for the thread, see the docs about that). No reason to incite potentially erroneous behaviour.

Or, with an example.

 class MyThread extends Thread {
     public void run() {
         throw new RuntimeException();
     }
 }

...

new MyThread().start();
// here thread dies silently with no visible effects at all

edit

Why can't the parent thread 'catch' the exception from the spawned 'child' thread?

@chaotic3quilibrium has already noted in his comment why not: because parent thread has likely moved on already.

new MyThread().start(); // launch thread and forget

// 1000 lines of code further...
i = i + 1; // would you like exception from child thread to be propagated here?
Gray
  • 115,027
  • 24
  • 293
  • 354
Nikita Rybak
  • 67,365
  • 22
  • 157
  • 181
  • Can you explain the reasoning behind the JVM carefully ignoring the exceptions in `run`? – R. Martinho Fernandes Dec 20 '10 at 16:34
  • 1
    @Martinho What else can it do? Suicide option sounds even worse: you wouldn't want the whole application to crash because of one thread. – Nikita Rybak Dec 20 '10 at 16:36
  • 5
    Silenty ignoring is perhaps misleading. The exception will be handled by the thread's UncaughtExceptionHandler, the default implementation of which will dump the stacktrace to System.err before killing the thread. As for why: How would the VM know what to do instead? – meriton Dec 20 '10 at 16:38
  • 1
    @Nikita: Well, I might. How dare the JVM decide which threads are essential and which are not? Quoting Eric Lippert on the CLR "you'd start up a bunch of worker threads, they'd all throw exceptions, and you'd end up with a running application with no worker threads left, doing no work, and not telling the user about it. It is better to force the author of the code to handle the situation where a worker thread goes down due to an exception; doing it the old way effectively hides bugs and makes it easy to write fragile applications." – R. Martinho Fernandes Dec 20 '10 at 16:40
  • Why can't the parent thread 'catch' the exception from the spawned 'child' thread? After all the JVM knows which parent thread is really interested about the exception? – Neel Dec 20 '10 at 16:42
  • @Martinho This is exactly why you should decide yourself which threads are important and give them proper exception handling :) JVM can't decide that for you. As for terminating the whole VM... I don't know, that sounds a bit like overreacting to me. – Nikita Rybak Dec 20 '10 at 16:43
  • @user: the "parent" thread may be long gone by that time. You have only two choices: ignore it, or bring the whole thing down. – R. Martinho Fernandes Dec 20 '10 at 16:43
  • @Nikita: But as meriton pointed out, the thread does not die silently with *no visible effects at all*. It runs the UncaughtExceptionHandler. That sounds reasonable to me. I personally prefer "bring the thing down" as approach when there's no handler, but having a handler is good enough. – R. Martinho Fernandes Dec 20 '10 at 16:45
  • @Martinho Well, I kinda mentioned exception handlers in my post. – Nikita Rybak Dec 20 '10 at 16:48
  • @NikitaRybak I really can't see how this example could work. If `MyThread` extends Thread then you have to override run() `Subclasses of Thread should override this method.` and the signature of run doesn't have a throws Exception therefore any error that you might throw from your `run` implementation will be truncated. Am I wrong? Is there a workaround? – Christos Karapapas May 19 '21 at 13:54
12

What would catch the exception and handle it? Let's assume that the run method could throw a checked exception. Then you could write code like this:

Thread myThread = new Thread(aRunnable);
try{
    myThread.start();
}
catch(Exception e){
   e.printStackTrace();
}
//do other stuff

BUT once you call myThread.start, the new thread is started in the background and the current thread continues and exits the try-catch and does other stuff. So if myThread did throw an exception later on, you can't catch it!

What you need to do is deal with the exception within the run method and then probably have a way of notifying another object that this thread failed.

dogbane
  • 266,786
  • 75
  • 396
  • 414
1

Suppose thread A starts up thread B. Then thread B throws an exception. You might think it would be nice for thread A to catch it. But where? By the time thread B thows the exception, who knows what thread A is doing? To take a trivial example, suppose we have this code in thread A:

try
{
  threadB=new PurgeAbandonedCarts();
  threadB.start();
}
catch (NullPointerException panic)
{
  ... handle errors purging abandoned carts ...
}
try
{
  processNewOrders();
}
catch (NullPointerException panic)
{
  ... handle problems in new orders ...
}
finally
{
  ... clean up ...
}

So we start up thread B to purge abandoned carts. Once it gets starte, we move on to processing new orders. Then thread B throws a null pointer exception. Should it be caught by the catch block associated with thread B, or the one associated with processing new orders?

If it goes to the new orders catch, it's likely that any code here has nothing to do with cleaning up problems with thread B. That can't be the right answer.

If you say the one associated with thread B, then that means that during the processing of new orders, control could suddenly be yanked out and sent back to try thread B catch block. But then what happenned to processing new orders? Do we just stop in the middle? Do we not even hit the finally block? And when we're done, do we then just keep executing and fall through to processing new orders again? Do we process orders twice? This can't be the right answer either.

Thus, there is nowhere to go if run throws an exception. The only logical thing to do is to have the run method catch any exceptions thrown itself, and handle them within the new thread.

Jay
  • 26,876
  • 10
  • 61
  • 112
0

throws declarations are part of the methods signature. To allow checked exceptions for Runnable#run, one had to declare them on the Runnable interface and had to try/catch everytime we start a thread.

Then again, we usually don't call the run method, we just implement it. We start() a Thread and then, somehow, the run method is called.

But the most obvious reason: When we start threads, we usually don't want to wait until the run method terminates just to catch exceptions like this:

try {
   new Worker().start();  // now wait until run has finished
} catch (SomeThreadException oops) {
   // handle checked exception
}
Andreas Dolk
  • 113,398
  • 19
  • 180
  • 268
0

The reason is that exception is thrown back to the caller. Caller of run() method is not your code. It is the Thred itself. So even if run() throws exception the program cannot catch it.

You should put thread execution result to some class level variable and then read it from there. Or alternatively use new API: executors and interface Callable that declares method call() that returns future result of the thread execution.

AlexR
  • 114,158
  • 16
  • 130
  • 208
0

The more obvious solution to the previous answers is that if you throw a checked exception, you are not correctly implementing run() as specified in the runnable interface.

It won't even compile:

run() in TestClass cannot implement run() in java.lang.Runnable; 
overridden method does not throw java.lang.Exception  
Benjamin Wootton
  • 2,089
  • 20
  • 21