Tag Archives: threads

Restarting Crashed Threads in Java

The biggest problem with Computer Science classes is that they don’t begin to prepare you for real-life problems. In real life, your program will most likely have to run for more than five minutes. It will almost always be subject to unexpected input. And it will definitely have to deal with more complex errors than java.lang.ArrayIndexOutOfBoundsException.

My current work with Next Big Sound deals with writing programs that run 24 hours a day, 7 days a week, that cannot fail for any reason. This is a rather daunting task, especially for someone who has never had to deal with real-world data before. One inherent problem with data in our world is that it is never reliable. You can never expect it to be what you want it to be, and it will almost certainly, at some point or another, trigger some error that will throw your entire system for a loop. There are several ways of dealing with these errors, but we will only deal with the most extreme of those here. If there is some error that despite your best efforts to control you simply cannot prepare for, the best solution may be just to start over.

In order to best explain this, I have included an example project below. I have tried to comment it well, but I will also give a short explanation here. The basic theory behind this method is to use something known as a ThreadGroup in order to manage a group of threads, some of which you think might die at some point. To handle this, we will use the method public void uncaughtException(Thread t, Throwable e). This method will get called when any thread that is a member of this thread group crashes due to some unhandled exception. When this happens, we will get access to the Thread object, through which we can get the Class associated with the thread. This will allow us to construct a new thread, start it, and join it, just as we did the first time, guaranteeing that if any thread dies, it will get replaced almost instantly.

To demonstrate this, I have included three code files below: a ParentThread class which handles all of the logic behind creating our application, creating our first thread, and creating replacement threads; a MyThreadGroup class, which implements the aforementioned uncaughtException(Thread t, Throwable e) method; and a CrashProneThread which is designed to crash unexpectedly after 5 seconds.

/**
 * Begin ParentThread.java
 */

package threadrevival;

import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * This class creates a new thread that is designed to die after approximately
 * 5 seconds of running.  At this point, a new thread should take its place. It
 * manages all of the logic behind joining threads and creating their replacements.
 *
 * @author Walter
 *
 */
public class ParentThread {

	private ConcurrentLinkedQueue<Thread> _threads;
	private ThreadGroup _threadGroup;

	/**
	 * Default constructor for the ParentThread class.
	 */
	public ParentThread()
	{
		// create a queue to hold our threads in, so we can join them as new ones get added
		_threads = new ConcurrentLinkedQueue<Thread>();

		// create out thread group to deal with thread crashes
		_threadGroup = new MyThreadGroup(this);

		// create one thread for our program
		CrashProneThread t = new CrashProneThread(_threadGroup, "First Thread");

		// add it to our list of threads
		_threads.add(t);

		// and start it
		t.start();

		// now, join this thread and all future threads before quitting.
		while(!_threads.isEmpty())
		{
			// remove the next thread
			Thread head = _threads.peek();

			// join the thread
			try {
				head.join();
			} catch (InterruptedException e) { }
			finally {
				// remove it from the queue
				_threads.remove(head);
			}

		}
	}

	/**
	 * This method creates a new thread.  This is a bit more complicated than the
	 * usual "Object object = new Object();" way of doing things, but it allows for
	 * a thread to replaced relatively easily.
	 *
	 * @param c	- The class of the dead thread.
	 */
	public void createNewThread(Class<?> c)
	{

		Thread t = null;
		try {
			// get the first constructor and pass in our parameters
			t = (Thread) c.getConstructors()[0].newInstance(_threadGroup, "ReplacementThread");
			t.start();
			_threads.add(t);
		}
		// Nothing we can do about this, but we must catch them.
		catch (IllegalArgumentException e) { }
		catch (SecurityException e) { }
		catch (InstantiationException e) { }
		catch (IllegalAccessException e) { }
		catch (InvocationTargetException e) {

		}

		// if we couldn't instantiate it, print out a message to the user
		if(t == null)
			System.out.println("Error instantiating thread of type: " + c.getName());

	}

	/**
	 * Create a new ParentThread() to run our application.  No arguments needed.
	 */
	public static void main(String[] args)
	{
		new ParentThread();
	}
}

/**
 * End ParentThread.java
 */

/**
 * Begin MyThreadGroup.java
 */

package threadrevival;

/**
 * This is our ThreadGroup.  It will detect with a thread unexpectedly dies
 * and will create a new one of the same type.
 *
 * @author Walter
 *
 */
public class MyThreadGroup extends ThreadGroup {

	private ParentThread _parent;

	/**
	 * This is our default constructor.
	 *
	 * @param parent - 	The ParentThread object through which to instantiate
	 * 					the replacement thread.
	 */
	public MyThreadGroup(ParentThread parent)
	{
		super("mythreadgroup");
		_parent = parent;
	}

	/**
	 * This method is called when an uncaught exception happens in a member
	 * thread of this ThreadGroup.
	 */
	public void uncaughtException(Thread t, Throwable e)
	{

		System.out.print(t.getName() + " died.  Creating a new thread.");
		_parent.createNewThread(t.getClass());

	}
}

/**
 * End MyThreadGroup.java
 */
/**
 * Begin CrashProneThread.java
 */

package threadrevival;

/**
 * This is a thread that is designed to run for 5 seconds before crashing.
 *
 * @author Walter
 *
 */
public class CrashProneThread extends Thread {

	/**
	 * This our default constructor.
	 *
	 * @param threadGroup	- The thread group this thread should join.
	 * @param name			- The name of this thread.
	 */
	public CrashProneThread(ThreadGroup threadGroup, String name)
	{
		super(threadGroup, name);
		System.out.println("Thread created: " + name);

	}

	/**
	 * This is our run method.  It prints out some messages to let the
	 * user know that it is running.  After about 5 seconds, it crashes
	 * on purpose.
	 */
	public void run()
	{
		System.out.print("Thread is running normally.");

		try {
			Thread.sleep(500);
		} catch (InterruptedException e1) { }

		// some code to make it look like its doing something
		for(int i = 0; i < 10; i++)
		{
			System.out.print(".");

			try {
				Thread.sleep(500);
			} catch (InterruptedException e) { }
		}

		System.out.println();

		System.out.println("Thread is about to crash.");

		try {
			Thread.sleep(500);
		} catch (InterruptedException e) { }

		throw new RuntimeException();
	}
}

/**
 * End CrashProneThread.java
 */

Please try it out and get a feel for this method. Although I just implemented this for the first time recently, I have used it in many places in order to help maintain the reliability of code that must run non-stop. In almost every situation I have run across, it can recover your program successfully from what would have become an unusable state. Enjoy!

Advertisements
Tagged , ,