9

Pros of hook methods:

beforeExecute(Thread, Runnable) and afterExecute(Runnable, Throwable)

beforeExecute(Thread, Runnable) and afterExecute(Runnable, Throwable) methods that are called before and after execution of each task. These can be used to manipulate the execution environment; for example, reinitializing ThreadLocals, gathering statistics, or adding log entries

I am using Custom ThreadPoolExecutor to handle uncaught exceptions. I can add try{} catch{} blocks in Runnable and Callable but assume a scenario where you can't force developer to add these blocks in relevant Runnable and Callable tasks.

This CustomThreadPoolExecutor , overrides afterExecute() method in ThreadPoolExecutor as below ( I have assigned variable b value to Zero to simulate arithmetic exception.

import java.util.concurrent.*;
import java.util.*;

class CustomThreadPoolExecutor extends ThreadPoolExecutor {

   public CustomThreadPoolExecutor() { 
       super(1,10,60,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(1000));
   }

   protected void afterExecute(Runnable r, Throwable t) {
     super.afterExecute(r, t);
     if (t == null && r instanceof Future<?>) {
       try {
         Object result = ((Future<?>) r).get();
         System.out.println(result);
       } catch (CancellationException ce) {
           t = ce;
       } catch (ExecutionException ee) {
           t = ee.getCause();
       } catch (InterruptedException ie) {
           Thread.currentThread().interrupt(); // ignore/reset
       }
     }
     if (t != null)
       t.printStackTrace();
   }
 }


public class CustomThreadPoolExecutorDemo{

    public static void main(String args[]){
        System.out.println("creating service");
        //ExecutorService service = Executors.newFixedThreadPool(10);
        CustomThreadPoolExecutor service = new CustomThreadPoolExecutor();
        service.submit(new Runnable(){
                 public void run(){
                    int a=4, b = 0;
                    System.out.println("a and b="+a+":"+b);
                    System.out.println("a/b:"+(a/b));
                    System.out.println("Thread Name in Runnable after divide by zero:"+Thread.currentThread().getName());
                 }
            });
        service.shutdown();
    }
}

Since submit() hides exception at framework, I have overridden afterExecute() method to catch Exception.

In this method, I added blocking call with below statement

 Object result = ((Future<?>) r).get();

Currently I have 10 threads with queue capacity as 1000. Assume that my Runnable takes 5 seconds to complete.

By overriding afterExecute() method, am I incurring any performance overhead OR any cons with this approach?

Ravindra babu
  • 37,698
  • 11
  • 250
  • 211
  • 2
    Guessing about performance is not a good thing, just benchmark the code with and without the override and check if there are relevant changes. http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java – Jack Feb 12 '16 at 15:19

2 Answers2

3

No, your blocking call wouldn't bring an overhead, because task is already completed its execution and has status >= NORMAL as you can see in void runWorker(Worker w)

beforeExecute(wt, task);
Throwable thrown = null;
try {
    task.run();
} catch (RuntimeException x) {
    thrown = x; throw x;
} catch (Error x) {
    thrown = x; throw x;
} catch (Throwable x) {
    thrown = x; throw new Error(x);
} finally {
    afterExecute(task, thrown);
}
dezhik
  • 990
  • 10
  • 16
  • But some times, my get() is blocking in scenarios when I have inter service communication issues ( service.submit() and call future.get()) – Ravindra babu Feb 12 '16 at 17:08
  • Yes, `get()` can block, but method `afterExecute(Runnable r, Throwable t)` is protected and (if you didn't change `FutureTask` semantics) is called in `ThreadPoolExecutor` _only_ after task is done. – dezhik Feb 12 '16 at 17:18
1

Better solution, hold on to the Future returned from submit() and then you can handle the exception in your main thread instead of hacking the executor to print it out for you.

Another alternative would be to use a common base Runnable which implements the exception handling that you desire, e.g.:

public abstract class BaseRunnable implements Runnable {
  public final run() {
    try {
      runImpl();
    } catch(Throwable t) {
      t.printStackTrace();
    }
  }
  protected abstract runImpl() throws Exception;
}
jtahlborn
  • 52,909
  • 5
  • 76
  • 118
  • This wouldn't work with `FutureTask`s which catches exception and `setException(ex);` and also wouldn't work with `CancellationException`. Exception will always be caught. – dezhik Feb 12 '16 at 17:00
  • @dezthink : Even Callable will work if you add below code in main thread ( I have done it earlier (before overidding afterExecute approach) : future.get() in try{} catch{} block – Ravindra babu Feb 12 '16 at 17:05
  • @ravindra my `wouldn't work` was about @jtahlborn suggestion to use `ThreadFactory` and `UncaughtExceptionHandler`, which he deleted during edit :) – dezhik Feb 12 '16 at 17:09
  • @ravindra - added an alternate solution for a simple way to add the exception handling to your Runnables. – jtahlborn Feb 12 '16 at 17:11
  • @dezhik - yeah, i remembered after the fact that the internals of the executor workings hide any exceptions from uncaught exception handlers. – jtahlborn Feb 12 '16 at 17:12
  • @jtahlborn I think `BaseRunnable` wouldn't cover the `CancellationException` case, because it wouldn't be thrown during execution I'm not saying it's bad, but it is slightly different from `afterExecute` implementation from the question. – dezhik Feb 12 '16 at 17:14
  • @dezhik - it effectively covers it (at least in a compatible manner). if the task was cancelled before it actually executed, then `afterExecute()` would never be called. if it was cancelled while running, the only _true_ indicatation of cancellation is if the run() method throws an exception. otherwise, the task effectively ran. (and cancellation doesn't seem to the be OP's use case considering the tasks are launched and not subsequently monitored using a Future). – jtahlborn Feb 12 '16 at 17:19
  • @jtahlborn Interesting I tested it, and it doesn't printStackTrace neither if `future.cancel(false);` nor `future.cancel(true)` is called. Because error is not thrown till `get()` occures – dezhik Feb 12 '16 at 17:30
  • 1
    @dezhik - correct, because your task will only _truly_ cancel if it detects the thread interrupt, and only certain operations do that automatically (like `wait` and `sleep`). in fact, the default cancellation semantics of ThreadPoolExecutor are a bit misleading, because they will tell you a task was cancelled even if the task is still happily executing, oblivious to the cancellation. – jtahlborn Feb 12 '16 at 17:43
  • @jtahlborn yep, thank you for such an interesting conversation :) – dezhik Feb 12 '16 at 17:45
  • @dezhik - no problem! – jtahlborn Feb 12 '16 at 17:45