Search code examples
javamultithreadingthread-safetythreadpoolinterruption

How to stop threads in Java


I'm coding a program that generates a random number in between 1 and 1,000. It then uses a thread pool of three threads to search certain number ranges in the broader 1 to 1,000 range. The threads check each number in their range, and compares it to the random target number, if it matches, the message to the console says so. If the number does not match, this is also reflected in the message to the console. I'm trying to figure out how to end the program once the target number is reached, and not continue to analyze numbers even though the target has already been found. Thank you.

This is the FindIt class:

/** fill in later */
public class FindIt extends Thread
{
    private int numToFind;
    private int numToStart;
    private int numToEnd;

    public FindIt( int nF, int nS, int nE )
    {
        numToFind = nF;
        numToStart = nS;
        numToEnd = nE;
    }

    public void run()
    {
        int counter = 0;

        int numAt = numToStart;

        for ( int i = 0; i < ( numToEnd - numToStart ) + 1; i++ )
        {
            counter++;

            if ( counter == 10 )
            {
                counter = 0;
                Thread.yield();
            }

            if ( numAt++ == numToFind )
            {
                System.out.println( "The target number, " + numToFind + ", has been found by " + Thread.currentThread().getName() + "." );
            }

            else
            {
                System.out.println( Thread.currentThread().getName() + " has analyzed the number " + ( numAt - 1 ) + " - not the target number." );
            }

        }   
    }
}

This is the program with the main method:

import java.util.Random; //imports Java's Random class
import java.util.concurrent.*;

/** fill in later */
public class NumberSearch
{
    public static void main( String [] args )
    {
        Random r = new Random(); //creates an instance of the Random class
        int randomNum = r.nextInt( 1001 ); //declares the integer randomNum and initilizes it to be a random interger in the range 0 inclusive to 1001 exclusive

        ExecutorService executor = Executors.newFixedThreadPool( 3 );

        FindIt find1 = new FindIt( randomNum, 0, 349);
        FindIt find2 = new FindIt( randomNum, 350, 699);
        FindIt find3 = new FindIt( randomNum, 700, 1000);

        executor.execute( find1 );
        executor.execute( find2 );
        executor.execute( find3 );

        executor.shutdown();
    }
}

Solution

  • Two things are required:

    • Make your tasks responsive to interruption by checking Thread.currentThread().isInterrupted() and having the task return when interruption is detected.

    • Use the ExecutorService's shutdownNow method in order to interrupt currently running tasks. The shutdown method makes the executor stop receiving new tasks but lets tasks already submitted to the executor run to completion.

    Don't subclass Thread for this, you should be extending Runnable or Callable in order to define tasks submitted to an Executor. Subclassing Thread means that the task allocates an OS thread, which is unnecessary since the actual threads are already created in the threadpool. For this example, since you're calculating a number in your tasks it might make sense to use Callable.

    There is an existing class in java.util.concurrent that is designed for this kind of thing. An example of canceling tasks once an answer has been found is given in the API documentation for ExecutorCompletionService.

    FindIt changed to detect interruption:

    public class FindIt implements Runnable
    {
        private int numToFind;
        private int numToStart;
        private int numToEnd;
    
        public FindIt( int nF, int nS, int nE )
        {
            numToFind = nF;
            numToStart = nS;
            numToEnd = nE;
        }
    
        public void run()
        {
            int counter = 0;
    
            int numAt = numToStart;
    
            for ( int i = 0; i < ( numToEnd - numToStart ) + 1; i++ )
            {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println(Thread.currentThread().getName() 
                    + " detected interruption, exiting");
                    return;
                }
                counter++;
    
                if ( counter == 10 )
                {
                    counter = 0;
                    Thread.yield();
                }
    
                if ( numAt++ == numToFind )
                {
                    System.out.println( "The target number, " + numToFind + ", has been found by " + Thread.currentThread().getName() + "." );
                }
    
                else
                {
                    System.out.println( Thread.currentThread().getName() + " has analyzed the number " + ( numAt - 1 ) + " - not the target number." );
                }
    
            }   
        }
    }