Lubos Programming Blog

Lubos Blog about Programming (mainly Java) and Computers

Stopping or interrupting Java hanging thread

Posted by lubosp on November 15, 2009

As most of Java developers know and experienced, some Java frameworks including core Java libraries do have nasty habit to run for very, very loooong time or hang indefinitely. Classic example is Java regex library which can cause stack overflow, or it can hang indefinitely.

In order to prevent the whole application from hanging and to allow to stop or interrupt the offending hanging thread, I am using timeout thread that checks if the watched thread finished in allotted time, and if it did not the timeout thread interrupts the watched, hanging thread.

Abruptly stopping or interrupting a thread from different thread is not well supported in Java especially when Thread.stop() method was deprecated, see How to stop a thread article.

But the problem is that Java offers nothing better to forcefully interrupt the offending hanging thread. Following is an example code of the TimeoutThread:

package com.lingoport.scanner.util;

import java.util.Date;

/** Stop a thread after a given timeout has elapsed
* <P>
* A simple timeout class.  You give it a thread to watch and a timeout
* in milliseconds.  After the timeout has elapsed, the thread is killed
* with a Thread.stop().  If the thread finishes successfully before then,
* you can cancel the timeout with a done() call; you can also re-use the
* timeout on the same thread with the reset() call.
* <P>
*
*/

public class TimeoutThread implements Runnable {

private final Thread targetThread;
private long millis;
private final Thread watcherThread;
private boolean loop;
private boolean enabled;
private static final boolean TIMEOUT_DISABLED = System.getProperty("disable.timeout") != null;

/**
* Constructor. Give it a thread to watch, and a timeout in milliseconds.
* After the timeout has elapsed, the thread gets killed. If you want
* to cancel the kill, just call done().
*
* @param targetThread
* @param millis
*/
public TimeoutThread(Thread targetThread, long millis) {
this.targetThread = targetThread;
this.millis = millis;
if (TIMEOUT_DISABLED) {
watcherThread = null;
enabled = false;
} else {
watcherThread = new Thread(this);
enabled = true;
watcherThread.start();
// Hack - pause a bit to let the watcher thread get started.
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}

/**
* Constructor, current thread.
*
* @param millis
*/
public TimeoutThread(long millis) {
this(Thread.currentThread(), millis);
}

/**
* Call this when the target thread has finished.
*/
public synchronized void done() {
loop = false;
enabled = false;
notify();
}

/**
* Call this to restart the wait from zero.
*/
public synchronized void reset() {
loop = true;
notify();
}

/**
* Call this to restart the wait from zero with a different timeout value.
*
* @param millis
*/
public synchronized void reset(long millis) {
this.millis = millis;
reset();
}

/*
* The watcher thread - from the Runnable interface.
* This has to be pretty anal to avoid monitor lockup, lost threads, etc.
*
*  (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public synchronized void run() {
if (TIMEOUT_DISABLED) return;
Thread me = Thread.currentThread();
me.setPriority(Thread.MAX_PRIORITY);
if (enabled) {
do {
loop = false;
try {
wait(millis);
} catch (InterruptedException e) {
}
} while (enabled && loop);
}
// The call stop() is deprecated, but Java doesn't offer anything better
if (enabled && targetThread.isAlive()) {
targetThread.stop();
done();
}
}

// Test main
public static void main(String[] args) {
System.out.println((new Date()) + "  Setting ten-second timeout...");
TimeoutThread tk = new TimeoutThread(10000);
try {
double f = 1.;
System.out.println((new Date()) + "  Starting execution of long loop...");
for(double i = 0; i < 1.0E99; i++) f = f * i;
System.out.println((new Date()) + "  Another execution of long loop...");
for(double i = 0; i < 1.0E99; i++) f = f * i;
tk.done();
} catch (Exception e) {
System.out.println((new Date()) + "  Caught Exception");
} catch (ThreadDeath td) {
System.out.println((new Date()) + "  Caught ThreadDeath");
}
System.out.println((new Date()) + "  Finished!");

}

}

This solution seems to be working quite well, and the offending hanging thread (caused for example by Java regex) can catch ThreadDeath exception, recover appropriately and continue work.

The only problem I encountered so far is that it doesn’t play well with Eclipse debugging, that is why I disable the TimoutThread while debugging the application using disable.timeout system property.

I know, that this is not great solution, but it works, and I couldn’t find any better solution (BTW, Thread.interrupt() does not work in this case). Let me know if you have better solution.

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: