Skip to content

Instantly share code, notes, and snippets.

@bsharathchand
Last active August 29, 2015 14:12
Show Gist options
  • Save bsharathchand/9ab88d5f5b13804e59ba to your computer and use it in GitHub Desktop.
Save bsharathchand/9ab88d5f5b13804e59ba to your computer and use it in GitHub Desktop.
Are the Initialization errors in child threads really eaten away by the Executor framework? --- No Definitely Not

Handling Initialization errors in Thread Pools using Executors f/w

If you are here, it means that there is a high chance that you spent alot of time debugging the initialization errors of your application when used with an ExecutorService thread pool in java; else you might just wanted to know more information to work with ExecutorServices in java.

Everyone of us knows that eating any exception without reporting is very dangerous, and should never be done.

  try{
  	// do something
	}catch(Exception e){
  }

But, the JDK seems to do this at the first sight when used with ExecutorService thread pools. Example code shown below.

Steps to Reproduce the Issue:

  1. Create a task that yeilds other tasks to be executed on other thread pools
  2. Execute the parent task in a parent thread pool
  3. Any exceptions raised by the child tasks yeilded will not be reported directly.
  4. Child tasks will not be executed and no errors shown in the consoles / logs.

Solutions to Fix the issue:

  1. When submitting the Parent task to executor, catch the Future<?> reference.
  2. Use the Future.get() method to check for any issues.
  3. Incase any exception has occured in creation / execution of child tasks, it will be wrapped in ExecutionException by the Future object.
  4. Now you should be able to log / handle the issues occurred in the child tasks.

Example below shows the complete implementation details for the same.

package com.novicehacks.learning;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

/**
 * ThreadPoolGist is an example class to show how to handle thread pools in an
 * orderly fashion, so that the initialization exceptions will not be eaten by
 * the framework.
 * 
 * <p>
 * <strong>Problem :</strong> When creating a ThreadPool from a thread created
 * by a parent Thread pool, any exceptions raised during the initialization of
 * the child threads will be eaten away by the framework
 * </p>
 * 
 * <p>
 * <strong>Solution :</strong> The idea is to catch the future object for every
 * task submitted to the executor service, irrespective whether it is a
 * {@link Runnable} or a {@link Callable} instance.
 * </p>
 * <p>
 * In addition to that we need to use {@link Future} objects {@link Future.get}
 * method to see whether any exceptions occurred during the execution of the
 * task.
 * </p>
 * 
 * @author Sharath Chand Bhaskara for novicehacks
 *
 */
public class ThreadPoolGist {
	/**
	 * A simple Runnable task that will take a String parameter, and throws
	 * IllegalArgumentException if its null during the initialization.
	 * 
	 * @author Sharath Chand Bhaskara for NoviceHacks!
	 *
	 */
	class Runner implements Runnable {
		String parameter;

		Runner(String param) {
			if (param != null) {
				this.parameter = param;
			} else {
				throw new IllegalArgumentException("Invalid Param");
			}
		}

		public void run() {
			System.out.println("Executing me with param : " + parameter);
		}

	}

	/**
	 * DoSomethingWithNull will initialize the Runner task with a null
	 * parameter, and tries to submit the task to a thread pool for execution.
	 * 
	 * <p>
	 * In this case the Initialization error from the Runner Task will never be
	 * reported, and the task will never be executed. Hence making it hard to
	 * debug.
	 * </p>
	 * 
	 * @throws InterruptedException
	 */
	public void doSomethingWithNull() throws InterruptedException {
		ExecutorService executor;
		executor = Executors.newCachedThreadPool();
		executor.submit(new Runner(null));
		executor.shutdown();
		executor.awaitTermination(1, TimeUnit.MINUTES);
	}

	/**
	 * DoSomethingWithoutNull will initialize the Runner task with a valid
	 * parameter, and tries to submit the task to a thread pool for execution.
	 * 
	 * <p>
	 * No issues to worry about in this case.
	 * </p>
	 * 
	 * @throws InterruptedException
	 */
	public void doSomethingWithoutNull() throws InterruptedException {
		ExecutorService executor;
		executor = Executors.newCachedThreadPool();
		executor.submit(new Runner("Hello"));
		executor.shutdown();
		executor.awaitTermination(1, TimeUnit.MINUTES);
	}

	/**
	 * An Executor is a Runnable task that is used to call the
	 * doSomethingWithNull and doSomethingWithoutNull method in the parent
	 * class.
	 * 
	 * <p>
	 * This is a parent task that is submitted to an executor, which yeilds more
	 * threads to be executed in other executors.
	 * </p>
	 * 
	 * @author Sharath Chand Bhaskara for NoviceHacks!
	 *
	 */
	class Executor implements Runnable {
		boolean value;

		Executor(boolean value) {
			this.value = value;
		}

		/**
		 * Based on the instance value this will call doSomethingWithNull or
		 * doSomethingWithoutNull methods asynchronously.
		 */
		public void run() {
			ThreadPoolGist gist;
			gist = new ThreadPoolGist();
			try {
				if (value) {
					gist.doSomethingWithNull();
				} else {
					gist.doSomethingWithoutNull();
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * The actual method which submits the Executor task in a thread pool.
	 * 
	 * @param value
	 * @throws InterruptedException
	 */
	public void doSomething(boolean value) throws InterruptedException {
		Executor executor = new Executor(value);
		ExecutorService service = Executors.newCachedThreadPool();
		service.submit(executor);
		service.shutdown();
		service.awaitTermination(2, TimeUnit.MINUTES);
	}

	/**
	 * The actual mehtod which submits the Executor task in a proper fashion to
	 * handle all the errors.
	 * 
	 * @param value
	 * @throws InterruptedException
	 * @throws ExecutionException
	 * @throws TimeoutException
	 */
	public void doSomethingCorrectly(boolean value)
			throws InterruptedException, ExecutionException, TimeoutException {
		Executor executor = new Executor(value);
		ExecutorService service = Executors.newCachedThreadPool();
		Future<?> future = service.submit(executor);
		service.shutdown();
		future.get(2, TimeUnit.MINUTES);
	}

	@Rule
	public ExpectedException ex = ExpectedException.none();

	/**
	 * This test method will not execute the task, and will also not report any
	 * error. This makes it very hard to debug in the real world applications.
	 * 
	 * @throws InterruptedException
	 */
	@Test
	public void testDoSomethingWithNull() throws InterruptedException {
		ThreadPoolGist gist;
		gist = new ThreadPoolGist();
		gist.doSomething(true);
	}

	/**
	 * This test method will not execute the task, but will report the errors.
	 * So that we can take a necessary action to correct the same.
	 * 
	 * @throws InterruptedException
	 * @throws ExecutionException
	 * @throws TimeoutException
	 */
	@Test
	public void testDoSomethingCorrectlyWithNull() throws InterruptedException,
			ExecutionException, TimeoutException {
		ThreadPoolGist gist;
		gist = new ThreadPoolGist();
		ex.expectMessage("Invalid Param");
		gist.doSomethingCorrectly(true);
	}

	/**
	 * This test method will execute the task without any issues.
	 * 
	 * @throws InterruptedException
	 */
	@Test
	public void testDoSomethingWithoutNull() throws InterruptedException {
		ThreadPoolGist gist;
		gist = new ThreadPoolGist();
		gist.doSomething(false);
	}
}
@bsharathchand
Copy link
Author

Execute this above code, for getting the clear picture of whats is happening.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment