Threads were briefly introduced in the post Threads in Java. Here, we continue the conversation by introducing a way to terminate threads once one in a set reaches a prescribed condition.
Thread termination example
The example below is a simple guessing game. A value between 1 and 1 billion is chosen (500 million in this case – line 119) and three threads (line 117) simultaneously “guess” based on their prescribed interval (lines 100-105). Once one correctly guesses, the method stopThreads(threadName)
(lines 69-73) is called, which sets the winner and interrupts all threads. Interruption alone is not sufficient to terminate the process. To do so gracefully, running threads must check their interruption status (Thread.currentThread().isInterrupted()
– line 100) and terminate their work (in this case, exit the while
loop).
import static java.lang.System.out;
import java.util.ArrayList;
import java.util.List;
/**
* This example illustrates one way to terminate threads. Here, a simple
* game of guess the value is levied against a series of threads that
* try to brute fore the answer. If one locates the value, all threads are
* terminated and the winner identified.
* @author Ray Hylock
*/
public class ThreadsTermination {
// global variables
private List<Thread> tList;
private String threadName;
/**
* Creates, executes, and waits for threads to finish. If the value
* is guessed, then the winner is identified.
* @param threads the number of threads
* @param N the number to inclusively search over
* @param V the value to find
* @throws InterruptedException
*/
private void execute(final int threads, final long N, final long V)
throws InterruptedException{
// create the threads
tList = new ArrayList<Thread>();
for(int i=0; i<threads; i++)
tList.add(new Thread(new CompThread(interval(N, i+1, threads), V),
"thread " + i));
// start threads - done separately to exclude instantiation if timing
threadName = null;
out.println("Searching for value: " + V);
for(Thread t : tList) t.start(); // calls run() in CompThread
// wait for them to finish
for(Thread t : tList) t.join();
// print the winner if one exists
if(threadName != null) out.println("Winner: " + threadName);
else out.println("The value was not identified");
}
/**
* Compute {@code thread}'s range within {@code N}. This is useful when
* segmenting a task into non-intersecting portions.
* @param N the value to segment
* @param thread the current thread number
* @param nthreads the number of threads
* @return a 2D array where the start (index 0) and end (index 1)
* values are placed
*/
private long[] interval(final long N, final int thread, final int nthreads){
long interval[] = new long[2];
interval[0] = (thread == 1) ? 0 : (N/nthreads)*(thread-1);
interval[1] = (thread == nthreads) ? N-1 : (N/nthreads)*thread - 1;
return interval;
}
/**
* Sets the winning thread and interrupts those currently executing.
* The threads will have to check the interrupted status
* {@link Thread#currentThread()#isInterrupted()} while iterating to
* terminate gracefully.
* @param threadName winning thread name
*/
private synchronized void stopThreads(String threadName){
this.threadName = threadName;
for(int i=0; i<tList.size(); i++)
if(tList.get(i).isAlive()) tList.get(i).interrupt();
}
/**
* The computational thread. Implements {@link Runnable}, so {@code run()}
* is executed when {@code start()} is invoked on an instantiated
* {@link CompThread} object.
*/
private class CompThread implements Runnable {
private long start, end; // range to compute
private long V;
/**
* Create a new computational thread.
* @param interval the start-to-end range
* @param V the value to find
*/
public CompThread (final long interval[], final long V){
// convert 0-index to 1-index (e.g., sum 1...100 instead of 0...99)
start = interval[0] + 1;
end = interval[1] + 1;
this.V = V;
}
@Override
public void run(){
boolean fnd = false;
// while still in range and not interrupted
while(start <= end && !Thread.currentThread().isInterrupted()){
if(start++ == V) {
fnd = true;
break;
}
}
// if found, terminate process and claim prize
if(fnd) stopThreads(Thread.currentThread().getName());
}
}
/**
* Main method.
* @param args command line arguments
* @throws InterruptedException
*/
public static void main(String args[]) throws InterruptedException{
int threads = 3;
long N = 1_000_000_000;
long V = N/2;
ThreadsTermination t = new ThreadsTermination();
t.execute(threads, N, V);
}
}
Output for the example:
Searching for value: 500000000
Winner: thread 1