如何在任何一个线程完成后终止所有其他正在运行的线程

时间:2016-04-26 18:09:36

标签: java multithreading

问题:我有一个并行开始循环的线程集合。首先退出任何线程后,必须终止所有其他正在运行的线程。这是我尝试过的但是它不起作用。任何帮助表示赞赏。

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();

        }

    }
}

2 个答案:

答案 0 :(得分:3)

这是一种方法,使用内部锁实现同步器,并使用中断来取消未完成的任务。数据结构使用户线程阻塞,直到生产者提交结果,然后取消其他工作线程。

这是一个玩具示例,请参阅最后的链接,了解实际操作方法。

首先,这是一个接受结果的线程安全数据结构,它允许线程注册为侦听器,并在提交结果后中断它们:

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);
    }
}

(我不喜欢让这堂课对听众有太多了解,但我也不想过度思考玩具的例子。)

wait方法释放锁并使调用线程处于休眠状态,直到发生通知(或者它可以随意停止等待)。它使用结果列表的size属性来了解提交结果的时间。假设因为一个线程停止等待你可以推断出当前状态的某些东西是不安全的,一旦线程重新获取锁定它就需要检查当前状态实际是什么。有关等待工作的详细信息,请参阅this tutorial

这是一个计算结果的任务(在迭代之间休眠,只是这些线程可以运行一段时间):

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);
        }
    }
}

请注意上面的代码中Runnable如何知道中断它的尝试。中断是合作的,任务负责决定何时检测中断和处理终止过程。

此外,如果任务涉及IO,那么在某些情况下,中断不起作用,您必须关闭套接字,请参阅this article以获取更多相关内容。

这是运行线程并获得结果的主程序。 MyQueue类已经完成了大部分工作,所以这不需要做太多工作:

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());
    }
}

请注意,这不是一场公平竞赛,因为线程如何?开始是错开的,后来的线程处于劣势。将任务提交给预先初始化线程池的执行程序将确保启动线程的时间不会导致延迟。

有关使用Executors和Futures等java.util.concurrent功能的更好方法,请参阅API documentation for ExecutorCompletionService中给出的示例。

答案 1 :(得分:-1)

一种简单的方法,使用synchronized类来处理循环条件:

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;
    }
}

在你的可运行的

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

        }
    }
}