【问题标题】:Communication between java threads: stopping all threads when one finishes its taskjava线程之间的通信:当一个线程完成任务时停止所有线程
【发布时间】:2023-03-29 21:20:01
【问题描述】:

我有 n 个线程并行运行,每个线程都执行一些自定义逻辑。但是,我的要求是,当任何一个线程完成执行时,所有其他线程都应该停止执行并返回。

实现这一点的最佳方法是什么?我想通过共享布尔变量来做到这一点。当任何线程完成执行时,它将设置布尔值。所有所有线程都会定期读取此变量并在设置时退出。

另外,我的自定义逻辑是一个无限循环,一旦我知道其他线程已完成执行,我想在当前迭代后停止执行。

这样做的正确方法是什么?

【问题讨论】:

标签: java multithreading


【解决方案1】:

使用ExecutorService 及其.invokeAny() 方法(注意:还有一个带有超时的版本)。

来自 Javadoc:

执行给定任务,返回已成功完成的任务的结果(即不抛出异常),如果有的话。

你有你的结果,.shutdown() 执行者。

请参阅Executors class 以获得适合您需求的执行器。

另一个解决方案是ExecutorCompletionService 类;在这种情况下,您将使用.take() 而不是.invokeAny(),并且必须逐个提交每个任务。而且您还必须保留对您的 ExecutorService 的引用,因为您需要一个作为参数,并且还需要将其关闭。

(注意:如果不返回结果,则创建Callable<Void> 实例)

【讨论】:

  • 感谢 fge。 ExecutorCompletionService 将依赖 Future.Cancel 并且仅当线程正在等待某些东西时它才会中断。现在在我的用例中,我有一个无限循环,并且希望在当前迭代完成后立即停止执行。在我的用例中,如果即使在取消之后也进行迭代,那就不好了。
  • “依赖中断”是什么意思?
  • @shadowfax 见上面的评论(不知道你有没有看到),我不明白你的问题。
【解决方案2】:

我更喜欢使用通用信号量来控制执行,并经常检查线程。

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 缓存其数据。如果您不将其标记为易失性,编译器可能会生成这样的字节码,它会优化字段中的 read,使其仅读取该字段一次,并将保存的值用于每次循环执行。

如果您的任务不同并且您实现了不同的循环,您可以使用任何外部public static volatile boolean 字段来保存标志。

此解决方案不依赖线程的等待状态。您可以检查循环中的多个位置isDone(甚至在每个代码块之前)。如果您保证您的代码将达到退出检查,则无论如何都无需中断线程。

【讨论】:

    【解决方案3】:

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

    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 状态。如果线程没有等待任何事情,这个解决方案将不起作用。
    • 然后你可以在每次迭代开始时做一个最小的定时等待。如果每个循环没有太多工作要做,那么时间延迟应该不是问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-29
    • 2011-04-13
    相关资源
    最近更新 更多