public class StopThread {
private static volatile boolean stopRequested;
public StopThread() {
Thread t = new Thread(new Runnable() {
public void run() {
int i = 0;
while (!stopRequested) i++;
}
});
t.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
public class StopThread {
private static volatile Thread currentThread;
public StopThread() {
Thread t = new Thread(new Runnable() {
public void run() {
int i = 0;
while (currentThread == Thread.currentThread()) i++;
}
});
currentThread = t;
t.start();
TimeUnit.SECONDS.sleep(1);
currentThread = null;
}
}
public class RestartableThread {
private AtomicInteger threadNumber = new AtomicInteger(0);
private volatile Thread currentThread;
public RestartableThread() {
startThread();
}
private void startThread() {
currentThread = new Thread(
new RestartableRunnable(), "Restartable thread " + threadNumber.getAndIncrement());
currentThread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable e) {
startThread();
logger.error(String.format("Thread '%s' has an uncaught exception and was restarted", t.getName()), e);
}
});
currentThread.start();
}
public void shutdown() {
currentThread = null;
}
private class RestartableRunnable implements Runnable {
public void run() {
while (true) {
if (currentThread != Thread.currentThread()) {
return;
}
try {
// Do a bit of work. Make sure this takes a limited time.
} catch (Exception ex) {
logger.error("exception", ex);
}
}
}
}
}
You can stop the thread by calling the shutdown() method. Failure to do so on program exit will prevent the JVM from stopping.
The runnable contains a try/catch to prevent the thread from dying. As a double fail safety mechanism, the uncaught exception handler is triggered for unhandled Throwables such as out of memory errors. The exception handler then simply starts a new thread so that execution continues. There are subtle advantages to this double approach. Exceptions are normally not so catastrophic, the thread could just continue. Out of memory errors however can be better dealt with by letting the thread terminate and make all its used memory available for the garbage collector.
If for any other reason you want to (re-)start the thread (for example because it is programmed to run only for a limited time), simply call startThread(). Make sure that the run condition is checked regularly. Consumer threads that wait on something like a queue should wake up regularly. For example, if you are waiting on new entries in a BlockingQueue this is better done with poll() then with get(). Conclusion
Despite the presence of the new and shiny executor services, it is still sometimes necessary to write your own thread. I hope that this little article will get you started on a robust yet flexible implementation. Next thread article: a helper for asynchronous cache updates.
I put the Thread's termination flag & work-queue together, as an object; and synchronize on that.
ReplyDeleteThis provides a single point of control for one, or multiple, threads.
Synchronization can be easily wrapped up, using 'synchronized' method modifiers..
Threads can synchronize & wait on all control conditions, as a group;
And control-conditions, can efficiently wake & notify one/or all threads on change.
Seems pretty sound, to me.
You should never use the synchronized keyword on methods. There are several reasons for not doing this, but the most important one is that you should minimize the amount of time that is spent in the synchronized block. There are many more subtle reasons.
ReplyDeleteIt is indeed possible to drop the volatile keyword when you need to synchronize on something else anyway.
Conditions are nice but a bit too advanced for this article.
You can also set a thread to daemon so it will not prevent the JVM from exiting.
ReplyDeleteYeah I know about daemon threads. Daemon threads only solve a tiny part of the problem, making them useful in just a few circumstances. As soon as your thread needs to do some data persistence, or something that simply needs to finish, declaring a thread as daemon won't help.
ReplyDeletethis article was extremly useful, thanks a lot!
ReplyDeleteps: you misspelled thread in the title