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 , ,

Ray Tracing

Intersecting With the Objects in Your Scene

The first and simplest step in a ray tracer is simply finding the objects in your scene and showing them on the screen.  For each pixel in your image, you have to send out a ray from the “eye” (the position of the virtual camera) through that pixel in order to determine what object, if any, it intersects with.  If the ray hits multiple objects, we simply take the closest one to the “eye.” The black areas are where the rays didn’t find any objects, while the colored circles are made up of pixels where the rays did find an object.  While there is no lighting, shadows, reflections, or texture yet, at least we have a sense of the way objects are layered — objects nearer to us are on top of those further away.

Adding Some Lights

Now that we know where our objects are in the scene, it is a simple upgrade to figure out how the light is playing off of them.  It turns out that the light we see is, for the most part, not dependent on where you are viewing it from (specular highlights are, however, but we aren’t that fancy yet).  This means that the only two important factors left are the surface normal at our intersection point and the direction of the light rays.  Using the angle between these two things, we can determine how much light falls on a particular pixel in the image.  This makes the 2D image we computed above look much more realistic by adding a sense of depth.

Shadows

In the above image, there are three lights positioned around your left shoulder.  Although we do see the light from these lights, we are currently seeing too much light.  In the above image, even if the lights were placed a million miles away, we would see the same amount of light.  Obviously, this is not how light works in real life, so we have to add some factor to scale it down as the light moves further away. We also need to account for situation where an object is blocking the light source — we need to worry about shadows.  Shadows are a rather simple add-on, given the functionality already present.  This time, instead of sending a ray from our “eye”, we will send it from the point of intersection with the object towards the light.  If we happen to find something in the way, we will then ignore that light.  The resulting image looks much more realistic.

Specular Highlights

The image above is looking better, but it still looks a little flat.  The spheres all look like they are painted with matte paint — they have absolutely no reflection or shininess.  We won’t tackle the first yet, but we can rather easily tackle the second.  As we mentioned before, our current lighting scheme doesn’t depend on the viewers position.  However, as we also mentioned, there is one important component that does depend on it: specular highlights.  When you look at a shiny surface, the highlights are a function of where the light is, the orientation of the object, and where you are as the viewer of the scene.  With this in mind, we can compute the amount of light that should form this specular highlight.  The result of this is shown below.

Reflections and Some Texture

For the sake of not making this post too long, I have combined these last two steps.  Though these spheres take it to the extreme, most objects in real life reflect light from other objects.  Even my desk reflects a little bit of what is sitting on it, though it may be hard to see at times.  Since the spheres in this scene are very shiny (as we can tell from the sharp specular highlights), it follows that the reflections should be quite clear.  In fact, the way the ray tracer has been coded, the reflections will be perfect.  This is not true for very many objects in real life, but adding the ability to have fuzzier reflections slows down the rendering by a substantial amount, so we have not dealt with that here.

The second thing we have done is to add an image wrapping our spheres in order to give them a bit more texture than they had above.  The resulting image looks much more realistic — the added texture gives the spheres a metallic look and feel, which is much better suited to the type of reflections and highlights we are generating.  The final result is shown below.