Home

Mohammed Ejaz

Overview

This library extends java.lang.Thread class to provide clean and reliable thread terminating capability.

The usual method prescribed by many, to simulate thread kill, is to use a control variable which thread needs to keep checking. When thread needs to be killed, it is sent an interrupt signal and the interrupt handler sets this variable to some value to indicate that thread needs to die. This can be difficult to implement - particularly in deeply nested methods. It also works only when thread is NOT blocked/busy in database call or waiting for socket or reading a file.

Note that the default interrupt function available on Thread works only when the thread is in sleep/wait state.

There is no way to interrupt a busy thread

The new approach uses control resource and thread shutdown hook. The control resource could be an open file or database connection or anything else which is crucial to thread's normal life cycle. If you close this resource, thread dies. The programmer adds (or removes) the hooks to close these control resources.

TerminateThread class extends Thread class. It keeps a stack of terminate hooks (per instance of TerminateThread) which are added by a programmer at run time. Thus when developer invokes interrupt on TerminativeThread's instance, it checks all the available hooks and invokes each one them in the order in which they were added

Here's how you use it ...

Test.java

Thread t1 = new TerminativeThread(new Worker(5080));
// Thread t1 = new Thread(new WorkerThread(5080));
t1.start();

Thread t2 = new TerminativeThread(new Worker(5081));
// Thread t2 = new Thread(new WorkerThread(5081));
t2.start();

logger.info("Sleep --");
Thread.sleep(1000 * 5);

logger.info("Calling kill --");
t1.interrupt();

logger.info("Sleep --");
Thread.sleep(1000 * 5);

logger.info("Calling kill --");
t2.interrupt();

Worker.java

// Add hook to set control variable
logger.info("Inside run() --");
TerminativeThread.push(new ThreadTerminateHook() {
    public void terminate() {
        logger.info("Set abort = true --");
        abort = true;
    }
});

try {
    while (!abort) { // abort is class member variable
        // Test Case : Thread is waiting for a socket
        /*
        try {
            final ServerSocket ssock = new ServerSocket(port);
            // Add hook to close this server socket
            TerminativeThread.push(new ThreadTerminateHook() {
                public void terminate() {
                    logger.info("Inside ThreadShutDownHook -- ");
                    try {
                        ssock.close();
                    } catch (Exception e) {
                    }
                    logger.info("Leaving ThreadShutDownHook -- ");
                }
            });

            ssock.accept();
        } catch (Exception e) {
            e.printStackTrace();
        }
        */

        // Test Case : Thread is busy reading from file (note there is no sleep!)
        final RandomAccessFile rdr = new RandomAccessFile("c:/docs/Test.txt");
        // Add hook to close the open file handle
        TerminativeThread.push(new ThreadTerminateHook() {
            public void terminate() {
                logger.info("Close rdr --");
                try {
                    rdr.close();
                } catch (Exception e) {
                }
            }
        });
        try {
            String line = null;                 
            while ((line=rdr.readLine()) != null) {
                ;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            Thread.yield();
            try { rdr.close(); } catch (Exception e) {}
            TerminativeThread.pop();
        }
    }
} catch (Throwable e) {
    e.printStackTrace();
} finally {
    TerminativeThread.clear();
}

logger.info("Leaving run() --");

Output

Oct 30, 2011 1:28:18 PM Test test
INFO: Starting Threads --
Oct 30, 2011 1:28:18 PM Test test
INFO: Sleep --
Oct 30, 2011 1:28:18 PM Worker run
INFO: Inside run() --
Oct 30, 2011 1:28:18 PM Worker run
INFO: Inside run() --
Oct 30, 2011 1:28:28 PM Test test
INFO: Calling kill --
Oct 30, 2011 1:28:28 PM open.ejaz.tthread.TerminativeThread interrupt
WARNING: TID 10 Inside interrrupt --
Oct 30, 2011 1:28:28 PM Worker$2 terminate
INFO: Close rdr --
java.io.IOException: Read error
at java.io.RandomAccessFile.read(Native Method)
at java.io.RandomAccessFile.readLine(RandomAccessFile.java:871)
at Worker.run(Worker.java:67)
at java.lang.Thread.run(Thread.java:662)
Oct 30, 2011 1:28:28 PM Worker$1 terminate
INFO: Set abort = true --
Oct 30, 2011 1:28:28 PM Test test
INFO: Sleep --
Oct 30, 2011 1:28:28 PM Worker run
INFO: Leaving run() --
Oct 30, 2011 1:28:35 PM Test test
INFO: Calling kill --
Oct 30, 2011 1:28:35 PM open.ejaz.tthread.TerminativeThread interrupt
WARNING: TID 11 Inside interrrupt --
Oct 30, 2011 1:28:35 PM Worker$2 terminate
INFO: Close rdr --
java.io.IOException: Read error
at java.io.RandomAccessFile.read(Native Method)
at java.io.RandomAccessFile.readLine(RandomAccessFile.java:871)
at Worker.run(Worker.java:67)
at java.lang.Thread.run(Thread.java:662)
Oct 30, 2011 1:28:35 PM Worker$1 terminate
INFO: Set abort = true --
Oct 30, 2011 1:28:35 PM Worker run
INFO: Leaving run() --


MongoDB Logo MongoDB