Java线程间通信:当一个线程完成任务时停止所有线程。

8
我有n个并行运行的线程,每个线程都执行一些自定义逻辑。然而,我的要求是当任何一个线程完成其执行时,所有其他线程都应该停止执行并返回。
最好的实现方式是什么?我考虑通过使用共享的布尔变量来实现这一点。当任何一个线程完成其执行时,它将设置该布尔值。所有线程定期读取此变量,并在其被设置时退出。
另外,我的自定义逻辑是一个无限循环,一旦我知道某个其他线程已经完成执行,我希望在当前迭代之后停止执行。
应该如何正确地做到这一点?

可能是Java中线程间通信的重复问题 - Anders R. Bystrup
从那个特殊的线程中生成所有其他线程!!! - AllTooSir
@AndersR.Bystrup 我并不认为这是一个很好的重复问题,它并没有真正回答这个问题。 - Duncan Jones
每次创建新的线程实例时,将该线程添加到全局线程列表中。这个线程列表将充当线程池。如果一个线程停止执行,您可以循环遍历线程池并停止每个线程。这是一个相当简单的解决方案,希望能对您有所帮助。 - ilya.stmn
你在那些“线程”中到底在做什么,为什么它们没有等待某些东西?它是一种数学计算,例如找素数吗? - gaborsch
3个回答

9
使用ExecutorService及其.invokeAny()方法(注意:还有一个带超时的版本)。
根据Javadoc:
执行给定的任务,返回已成功完成(即未抛出异常)的一个结果。
获得结果后,请.shutdown()执行程序。
请查看Executors以获取符合您需求的执行程序。
另一个解决方案是ExecutorCompletionService类;在这种情况下,您应该使用.take()而不是.invokeAny(),并且必须逐个提交每个任务。您还需要保留对ExecutorService的引用,因为需要将其作为参数,并且还需要关闭它。
(注意:如果不返回结果,请创建Callable<Void>实例)

感谢fge。ExecutorCompletionService将依赖于Future.Cancel,只有在线程等待某些内容时才会中断。现在在我的用例中,我有一个无限循环,并希望在当前迭代完成后立即停止执行。在我的用例中,如果迭代甚至在取消后继续进行,那就不好了。 - shadowfax
“rely on interrupt” 是什么意思? - fge
@shadowfax,见上面的评论(我不知道你是否看到了),我不明白你的问题。 - fge

1
我希望使用常见的信号量来控制执行,并在线程中频繁检查。
public class MyTask implements Runnable {
    private static volatile boolean isDone = false
    @Override
    public void run() {
        while(true) {
            if (isDone) {
                break;
            }

            // do some calculation, no wait() s

            if (...has result...) {
                isDone = true;
                break;
            }
        }
    }
}

Thread t1 = new Thread(new MyTask());
Thread t2 = new Thread(new MyTask());
Thread t3 = new Thread(new MyTask());
t1.start();
t2.start();
t3.start();

唯一需要注意的是static volatile boolean变量。 Static是因为所有线程都必须访问相同的标志,volatile是为了防止JVM缓存其数据。如果您不将其标记为volatile,则编译器可能会生成这样的字节码,即它会优化从字段中读取的方式,以便仅读取该字段一次,并将保存的值用于每个循环执行。
如果您的任务不同并且实现不同的循环,则可以使用任何外部的public static volatile boolean字段来保存该标志。
此解决方案不依赖于线程的wait状态。您可以在循环中的多个位置检查isDone(甚至在每个代码块之前)。如果您保证您的代码将达到退出检查,则无需中断线程。

0

你可以将所有的线程对象保存在一个共享的全局数组中,然后在每个任务结束时调用一个“清理”函数,该函数只需获取这些对象并对它们执行“中断”调用。这将导致第一个线程完成并执行其余线程的执行。

public class MyMain
{
   public static Thread workers[NUM_WORK];
   public static void main(..)
   {
      MyMain.workers[0] = new Thread(new MyTask0());
      .
      .
      .
      MyMain.workers[n] = new Thread(new MyTaskN());
      //Then start them all..
      // And wait for them unless you want them to die before execution ;)
   }
}

然后在你的其他工作人员上

import MyMain;
public class MyTaskI implements Runnable
{
   public void run()
   {
      //Do all the work
      for(Thread t : MyMain.workers)
      {
         // If it is not this thread
         t.interrupt();
      }
   }
}

如果我犯了任何语法错误,对不起。我已经有一段时间没有写Java了,但是我的意思是让你明白的 ;)


正如OP所提到的,thread.interrupt()依赖于wait状态。如果线程没有在等待任何东西,那么这个解决方案将不起作用。 - gaborsch
然后,您可以在每个迭代的开头进行最小化的定时等待。如果每个循环要执行的工作不多,则时间延迟不应该是问题。 - cgledezma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接