How to Stop Threads in Java. Best Practices and Examples.

Oct 22, 2016 · 8 mins read

Let’s get this straight: in Java, there’s no straightforward or guaranteed way of stopping threads. You can only request a thread to stop itself and depending on its design, it can ignore your request and carry on. In this article, we’ll explore a few ways to stop threads and how you can design your tasks which run in threads to follow best practices.

In the early versions of Java, its designers made a mistake and decided they’d support thread termination. They added Thread.stop(), Thread.suspend() methods. A while later, they realized their mistake. These methods are inherently unsafe and could lead to dangerous side-effects. Imagine killing a thread that’s in the middle of writing a file to disk. You might end up corrupting the file. So they made the right call and deprecated these methods.

In Java, stopping threads requires cooperation from the task that’s being run by the thread. This co-operative mechanism is based on interruption. It’s a simple concept: to stop a thread, we deliver it an interrupt signal, requesting that the thread stops itself at the next available opportunity. To send the interrupt signal, we can use Thread.interrupt() method. It is up to the thread/task to check the signal, clean up if necessary and stop itself. It is totally possible (although a bad design) that the task may not bother to check the interrupt signal at all or ignore it. There’s very little an external caller can do in this situation. (I cover non-standard cancellation at the end of this post.)

Thread.Interrupt() signal

A thread in Java could be interrupted by by external callers using the Thread.interrupt() method. Well designed tasks running in threads must check for interruption at regular intervals using Thread.isInterrupted(). The only time you can skip checking for interrupts within your tasks is if it’s short lived and guaranteed to complete within a few seconds. The following example illustrates how to use it.

public static void main(String[] args) throws Exception {
    /**
    * A Thread which is responsive to Interruption.
    */
    class ResponsiveToInterruption extends Thread {
        @Override public void run() {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("[Interruption Responsive Thread] Alive");
            }
            System.out.println("[Interruption Responsive Thread] bye**");

        }
    }

    /**
    * Thread that is oblivious to Interruption. It does not even check it's
    * interruption status and doesn't even know it was interrupted.
    */
    class ObliviousToInterruption extends Thread {
        @Override public void run() {
            while (true) {
                System.out.println("[Interruption Oblivious Thread] Alive");
            }
            // The statement below will never be reached.
            //System.out.println("[Interruption Oblivious Thread] bye");
        }
    }

    Thread theGood = new ResponsiveToInterruption();
    Thread theUgly = new ObliviousToInterruption();

    theGood.start();
    theUgly.start();

    theGood.interrupt(); // The thread will stop itself
    theUgly.interrupt(); // Will do nothing
}
[code language="shell"]
[Interruption Oblivious Thread] Alive
[Interruption Responsive Thread] Alive
[Interruption Responsive Thread] Alive
[Interruption Oblivious Thread] Alive
[Interruption Responsive Thread] bye**
[Interruption Oblivious Thread] Alive
[Interruption Oblivious Thread] Alive
[Interruption Oblivious Thread] Alive
[Interruption Oblivious Thread] Alive
....

Blocking Methods and Interruptions

As we’ve seen, well designed, long-running tasks check for interrupts at regular intervals - e.g. as a loop condition - and act on it. But what about the blocking methods you call inside your loop? These methods “block” and might take a long time to return, effectively delaying the calling thread’s ability to check for interruption in a timely manner.

Thread.sleep(), BlockingQueue.put(), ServerSocket.accept() are some examples of blocking methods.

Blocking Methods that Support Interruption

Most blocking methods support interruptions. They usually respond by throwing InterruptException or ClosedByInterruptionException when they detect interruptions.

Let us consider an example. the code below calls Thread.sleep(). When it detects interruption, Thread.sleep() throws InterruptedException and the caller exits the loop. All blocking methods which support InterruptedException also clear the interrupted status. You must either act on interruption when you catch this exception or set the interrupted status to give the code higher up the stack a chance to act on interruption.

@Override
public void run() {
    while(true) { // Long running loop
        try {
            Thread.sleep(Long.MAX_VALUE);
        } catch (InterruptedException exit) {
            break; //End the loop on interruption
        }
    }
}

InterruptedException should not be ignored. It’s a bad practice to to just swallow the exception and do nothing.

Blocking Methods that do not Support Interruption

Some blocking methods such as the built-in java.net.Socket don’t support interruptions. So how can you interrupt a ServerSocket.accept() method? We can do this by calling Socket.close() from another thread, forcing the Socket.accept() to throw an exception and exit out of the blocking call. Here’s an example:

/**
* Thread that checks for interruption, but calls a blocking method
* that doesn't detect Interruptions.
*/
class InterruptibleShesNotReally extends Thread {

    @Override
    public void run() {
        while(!Thread.currentThread().isInterrupted()) {
            try {
                ServerSocket server = new ServerSocket(8080);
                Socket client = server.accept();// This method will not
                                                // return or 'unblock'
                                                // until a client connects

            } catch (IOException ignored) { }   // Do not do in practice!
        }
    }
}
package kitchensink;

import java.net.*;
import java.io.*;

/**
 * Demonstrates non-standard thread cancellation.
 *
 * @author umermansoor
 */
public class SocketCancellation {

    /**
     * ServerSocket.accept() doesn't detect or respond to interruption. 
     * The class below overrides the interrupt() method to support 
     * non-standard cancellation by canceling the underlying 
     * ServerSocket forcing the accept() method to throw Exception, 
     * on which we act by breaking the while loop.
     */
    static class CancelleableSocketThread extends Thread {

        private final ServerSocket server;

        public CancelleableSocketThread(int port) throws IOException {
            server = new ServerSocket(port);
        }

        @Override
        public void interrupt() {
            try {
                server.close();
            } catch (IOException ignored) {
            } finally {
                super.interrupt();
            }
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Socket client = server.accept();
                } catch (Exception se) {
                    break;
                }
            }
        }
    }

    /**
     * Main entry point.
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        CancelleableSocketThread cst = new CancelleableSocketThread(8080);
        cst.start();
        Thread.sleep(3000);
        cst.interrupt();
    }
}

Summary

Threads cannot be stopped externally in Java; they can only be delivered a signal to stop. It is up to the thread to: 

  1. check the interruption flag regularly, and,
  2. to act upon it

A well designed task that is long running checks for interrupts at regular intervals and acts on interrupt signals.

Blocking methods come in two flavours: those that support interruptions and those that do not. Blocking methods that support interruptions such as BlockingQueue.put() throw InterruptedException or ClosedByInterruptionException. Blocking methods that do not support interruptions require that we get non-standard cancellation mechanisms and use creative ways. In the example above, I showed how to exit out of Server.accept(), a blocking method that ignores interruptions.

Hope you found this article useful. The following video is an excellent tutorial on the topic.

#java #multithreading

You May Also Enjoy


If you like this post, please share using the buttons above. It will help CodeAhoy grow and add new content. Thank you!