简体   繁体   中英

How to terminate all other running threads after any of one thread is finish

Problem: I have collection of threads start in a loop parallelly. After exiting anyone of thread first ,all other running threads must be terminated. This is what I tried but it doesn't work. Any help is appreciated.

public class ThreadsMain {

    public static void main(String[] args) {
        int SIZE = 3;
        Thread t[] = new Thread[SIZE];

        for (int i = 0; i < SIZE; i++) {
            myThreads th = new myThreads();
            t[i] = new Thread(th);
            t[i].start();

        }

    }
}

Here is one way to do it, with a synchronizer implemented with intrinsic locks, and using interruption to cancel the unfinished tasks. The data structure makes a consumer thread block until a producer has submitted a result, then it cancels the other worker threads.

This is a toy example, see the link at the end for the real-world way to do this.

First, here's a threadsafe data structure that accepts results, it allows threads to register as listeners and interrupts them once it has a result submitted to it:

class MyQueue<T> {
    private java.util.List<T> results = new java.util.ArrayList<T>();
    private java.util.List<Thread> listeners = new java.util.ArrayList<Thread>();

    public synchronized void put(T o) {
        results.add(o);
        notifyAll();
        for (Thread listener : listeners) {
            listener.interrupt();
        }
    }

    public synchronized T take() throws InterruptedException {
        while (results.size() == 0) {
            wait();            
        }
        return results.remove(0);
    }

    public synchronized void addListener(Thread t) {
        listeners.add(t);
    }
}

(I don't like having this class know so much about the listeners but I don't want to overthink a toy example either.)

The wait method releases the lock and makes the calling thread go dormant until a notification occurs (or it can just stop waiting arbitrarily). It uses the size property of the results list to know when a result has been submitted. It's not safe to assume that because a thread stopped waiting that you can infer something about the current state, once the thread reacquires the lock it needs to check what the current state actually is. For more about how wait works see this tutorial .

Here's a task that calculates a result (sleeping between iterations just so these threads can run for a while):

class FibTask implements Runnable {

    private final MyQueue<BigInteger> queue;
    private final int n;
    private long sleepTime;

    public FibTask(int n, long sleepTime, MyQueue<BigInteger> queue) {
        this.n = n;
        this.sleepTime = sleepTime;
        this.queue = queue;
    }

    @Override public void run() {
        BigInteger a = BigInteger.valueOf(0);
        BigInteger b = BigInteger.valueOf(1);
        int i = 0;
        try {
            while (!Thread.currentThread().isInterrupted() && i < n) {
                i = i + 1;
                BigInteger temp = a;
                a = b;
                b = a.add(temp);
                Thread.sleep(sleepTime);
            }    
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        if (!Thread.currentThread().isInterrupted()) {
            queue.put(b);
        }
    }
}

Notice in the code above how the Runnable needs to be aware of attempts to interrupt it. Interruption is cooperative, the task is responsible for deciding when to detect interruption and for handling the termination process.

Also if a task involves IO then in some cases interruption doesn't work and you have to close the socket, see this article for more discussion of this.

Here's the main program that runs the threads and gets the result. The MyQueue class is already doing most of the work so this doesn't have to do much:

class Completion {
    public static void main(String ... args) throws Exception {
        MyQueue<BigInteger> queue = new MyQueue<BigInteger>();
        Thread t1 = new Thread(new FibTask(10, 1000L, queue));
        Thread t2 = new Thread(new FibTask(20, 10000L, queue));
        Thread t3 = new Thread(new FibTask(25, 50000L, queue));
        queue.addListener(t1);
        queue.addListener(t2);
        queue.addListener(t3);
        t1.start();
        t2.start();
        t3.start();
        System.out.println(queue.take());
    }
}

Be aware this isn't a fair race because of how the threads' starts are staggered, later threads are at a disadvantage. Submitting tasks to an Executor that initializes a threadpool up front would make sure that the time to start a thread didn't cause a delay here.

For a better way that makes use of java.util.concurrent features like Executors and Futures, see the example given in the API documentation for ExecutorCompletionService .

A simple approach, use a synchronized class to handle the loop condition:

class ThreadHandler
{
    static Object lock = new Object();
    static boolean finished = false;

    static void finishThreads()
    {
        synchronized(lock)
        {
            finished = true;
        }
    }

    static boolean isFinished()
    {
        boolean result;
        synchronized(lock)
        {
            result = finished;
        }
        return result;
    }
}

And in your runnable

class myThreads implements Runnable
{
    @Override
    public void run()
    {
        while(!ThreadHandler.isFinished())
        {

        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM