【问题标题】:Combining two Runnable objects组合两个 Runnable 对象
【发布时间】:2011-01-11 19:58:39
【问题描述】:

例如,假设我有一个名为 RunnableA 的 Runnable,它可以做一些事情。我还有一个名为 RunnableB 的 Runnable,它可以做其他事情。有没有办法以某种方式将这两个 Runnable 组合起来,以便它们在同一个线程中运行?

问题的第二部分是,如果这是可能的,那么我可以指定它们运行的​​顺序吗?

编辑!:我想这样做的原因是因为我需要在 EDT 上运行代码,但其他一些代码需要在另一个线程上运行。请看下面的代码。

类似的东西

public final class CompoundRunnable implements Runnable { private final Iterable runnables; public CompoundRunnable(Iterable runnables) { // From Guava. Easy enough to do by hand if necessary this.runnables = Lists.newArrayList(runnables); } public CompoundRunnable(Runnable... runnables) { this(Arrays.asList(runnables)); } @Override public void run() { for (Runnable runnable : runnables) { runnable.run(); } } } public void setStatusAndProgress(final String status,Runnable runnable) { Runnable startUpRunner = new Runnable() { public void run() { SwingUtilities.invokeLater(new Runnable() { public void run() { setStatus(status); selfReference.getProgressBar().setIndeterminate(true); } }); } }; Runnable cleanUpRunner = new Runnable() { public void run() { SwingUtilities.invokeLater(new Runnable() { public void run() { setStatus(""); getProgressBar().setIndeterminate(false); } }); } }; Runnable theRunner = new CompoundRunnable(startUpRunner,runnable,cleanUpRunner); new Thread(theRunner).start(); }

抱歉,如果没有很好地解释,如果您需要澄清,请发布 cmets。

谢谢!

【问题讨论】:

  • 如果您需要这些在同一个线程上按指定的顺序运行,这不完全是多线程问题。你可以从可运行文件中提取你的代码吗?

标签: java multithreading runnable green-threads


【解决方案1】:

我很惊讶没有人建议更灵活的方法,与现有答案相比,这恰好也是最简单、最短的解决方案。

只需创建一个新类RunList 子类化一个具体的List 实现(例如ArrayList),然后声明它实现Runnable

public class RunList extends ArrayList<Runnable> implements Runnable {
    @Override
    public void run() {
        for (Runnable runner : this) {
            runner.run();
        }
    }
}

定义了该类后,您就可以开始定义了:

RunList runList = new RunList() 

然后使用您要运行的所有Runnables 填充runList。现在,您可以在此处使用完整的 Java 语言和标准库来对列表进行重新排序或一般操作。

下面包含一些示例(未经测试)。

public void testRunList() {

    RunList list = new RunList();

    // the venerable, straight-forward technique
    for (int i = 0; i < 10; ++i) {
        list.add(() -> {
            /* i'th runnable */
        });
    }

    // a bit of type juggling to concisely add an arbitrary number of Runnables to the list
    list.addAll(Arrays.asList(
        () -> { /* runnable 1 */ },
        () -> { /* runnable 2 */ }
    ));

    // or if you're just feeling frisky, these methods too are at your disposal!
    Collections.shuffle(list);

    list.run();
}

【讨论】:

    【解决方案2】:

    你当然可以创建一个Runnable,它只运行一个runnable然后另一个:

    public final class CompoundRunnable implements Runnable
    {
        private final Runnable first;
        private final Runnable second;
    
        public CompoundRunnable(Runnable first, Runnable second)
        {
            this.first = first;
            this.second = second;
        }
    
        @Override
        public void run()
        {
            first.run();
            second.run();
        }
    }
    

    更一般地说,您可以将其设为 Iterable&lt;Runnable&gt;,复制所有 Runnable 引用,然后按顺序运行它们。例如:

    public final class CompoundRunnable implements Runnable
    {
        private final Iterable<Runnable> runnables;
    
        public CompoundRunnable(Iterable<Runnable> runnables)
        {
            // From Guava. Easy enough to do by hand if necessary
            this.runnables = Lists.newArrayList(runnables);
        }
    
        public CompoundRunnable(Runnable... runnables)
        {
            this(Arrays.asList(runnables));
        }
    
        @Override
        public void run()
        {
            for (Runnable runnable : runnables)
            {
                 runnable.run();
            }
        }
    }
    

    【讨论】:

    • 非常感谢。我对您的代码进行了一些修改,并在上面的示例中使用了它。你能告诉我你的想法吗?
    • @user489041:好吧,你一开始就删除了泛型……为什么?
    • @Jon Skeet 我无法使用函数 Lists.newArrayList(runnables)。这是 com.google.common.collect.Lists 的一部分吗?
    • @user489041:是的。但即使你想自己创建一个新的ArrayList 并手动完成,仍然值得使用泛型。
    • @user489041:泛型基本上是关于类型安全的——例如,确保您不要尝试将字符串放入整数列表中。我强烈建议您阅读它们。
    【解决方案3】:

    另一种变化。

    public static Runnable combineRunnables(final Runnable... runnables) {
       return new Runnable() {
            public void run() {
                for(Runnable r: runnables) r.run();
            } 
       };
    }
    

    【讨论】:

      【解决方案4】:

      当然。只需创建另一个 runnable,它调用两个现有 Runnable 的 run 方法作为其实现的一部分。您可以使用 Runnable 列表等使包装 Runnable 专用或通用。

      public Runnable combineRunnables(Runnable a, Runnable b)
      {
         Runnable retVal = new Runnable()
         {
              public void run()
              {
                  a.run();
                  b.run();
              } 
         };
      
         return retVal;
      }
      

      也许让您感到困惑的是 Runnable 与线程的关联。 Runnable 接口不绑定到线程,并且它本身没有任何线程语义,因此您可以像上面那样轻松组合它,而不会因线程状态而陷入困境。

      【讨论】:

      • 是的,我想这就是我感到困惑的原因。我想我想多了。我一直在寻找一种通用方法,可以在 EDT 上启动和关闭代码,然后在其自己的线程中运行一些工作代码。这就是为什么我想知道我是否可以以这种方式组合它们。我根据回复的尝试已在上面发布。
      • 可能应该从第一个捕获异常并将其传递给未捕获的异常处理程序。很遗憾,Runnable 的意义如此之小。
      【解决方案5】:

      是的,您当然可以将它们结合起来。但总的来说,除了调用其他对象来实际完成工作之外,runnable 内部不应该有太多内容。你能不能只获取你需要的可运行文件的“内部”并在你想要的上下文中执行它们。

      如果您想确保事情只是一个接一个地执行,而不是同时执行,您可以使用 SingleThreadExecutorService 并为它们提供 Runnables。它会一次执行一个。

      如果你真的想运行多个可运行文件,那么你可以这样做(第一个 RuntimeException 将退出)。

      注意静态方便方法,这样你就可以说“new Thread(CompositeRunnable.combine(a,b,c,d....))”

      public class CompositeRunnable implements Runnable {
      
          private final Runnable[] runnables;
      
          public CompositeRunnable(Runnable... runnables) {
              this.runnables = runnables;
          }
      
          public void run() {
              for (Runnable runnable : runnables) {
                  runnable.run();
              }
          }
      
          public static Runnable combine(Runnable... runnables) {
              return new CompositeRunnable(runnables);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2019-06-10
        • 2021-03-08
        • 1970-01-01
        • 2015-08-06
        • 2020-02-04
        • 2015-01-26
        • 1970-01-01
        • 2022-01-04
        • 1970-01-01
        相关资源
        最近更新 更多