【问题标题】:Java - multiple Runnables per ThreadJava - 每个线程有多个 Runnables
【发布时间】:2011-06-30 11:05:02
【问题描述】:

我有固定数量的线程。我希望每个线程一个接一个地运行三个Runnables。这里有一些伪代码来解释:

Thread[] threads = new Thread[4];

for (int i = 0; i < threads.length; i++) {
    // Set the first tasks.
    threads[i] = new Thread(new FirstRunnable());
    threads[i].start();
}

for (int i = 0; i < threads.length; i++)
    threads[i].join(); // wait until the first tasks are done

for (int i = 0; i < threads.length; i++) {
    // Set the second task.
    threads[i].setRunnable(new SecondRunnable());
    threads[i].start();
}

for (int i = 0; i < threads.length; i++)
    threads[i].join(); // wait until the second tasks are done

...

使用ThreadPool 听起来有点矫枉过正,尤其是因为我要追求性能、性能、性能。在 Java 中实现这一点的最佳方法是什么?

【问题讨论】:

标签: java multithreading runnable


【解决方案1】:

每当您看到new Thread(...).start() 时,请使用Executors 框架。特别是使用Executors.newFixedThreadPool(...)。

【讨论】:

  • 为了完整起见:Executors 是一个实用程序类。为多线程捆绑所有高级结构的框架或更确切地说是包是 java.util.concurrent 包。
【解决方案2】:

似乎对Executors 类中的 newFixedThreadPool 很有用。

所以你的代码看起来像:

ExecutorService es = Executors.newFixedThreadPool(4);
List<Future> futures = new ArrayList<Future>();
for (int x = 0; x < 4; x ++) {
    futures.add(es.submit(new FirstRunnable()));
}
while (futures.size() > 0) {
   futures.remove(0).get();
}
for (int x = 0; x < 4; x ++) {
    futures.add(es.submit(new SecondRunnable()));
}

while (futures.size() > 0) {
   futures.remove(0).get();
}

当然,您可以轻松地重构上面的代码以消除代码重复。

【讨论】:

  • 您可以使用 CompletionService 来避免显式构建和维护 Future 列表。
【解决方案3】:

您可以使用CyclicBarrier 和“CombinedRunnable”,如下所示。屏障允许线程在继续执行下一个可运行对象之前相互等待。

CyclicBarrier barrier = new CyclicBarrier(4);
Runnable r = new CombinedRunnable(barrier, new FirstRunnable(), new SecondRunnable());
Thread[] threads = new Thread[4];
for (int i = 0; i < threads.length; i++) {
    threads[i] = new Thread(r);
    threads[i].start();
}

CombinedRunnable 类:

public class CombinedRunnable implements Runnable{

    private final CyclicBarrier barrier;
    private final Runnable[] runnables;

    public CombinedRunnable(CyclicBarrier barrier, Runnable... runnables){
        this.barrier = barrier;
        this.runnables = runnables;
    }

    /* (non-Javadoc)
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        for(Runnable r: runnables){
            r.run();
            try {
                barrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
}

【讨论】:

    【解决方案4】:

    实现此目的的惯用方法是将Executor 与CompletionService 结合使用。这允许您将许多工作单元映射到固定大小的线程池,并提供一种优雅的阻塞机制,直到所有工作完成。

    请注意,您对使用线程池可能会如何影响效率的担忧并不是真正的问题:主要开销是创建单个线程,无论如何您都在这样做;创建池时额外的对象创建开销可以忽略不计。

    // Create fixed thread pool and wrap in a CompletionService to allow for easy access to completed tasks.
    // We don't have an explicit result for each Runnable so parameterise the service on Void.
    CompletionService<Void> cs = new ExecutorCompletionService<Void>(Executors.newFixedThreadPool(3));
    
    // Create units of work for submission to completion service.
    Runnable[] runnables = ...
    
    // Submit runnables.  Note that we don't care about the result so pass in null.
    for (Runnable r : runnables) {
      cs.submit(r, null);
    }
    
    // Take each *completed* result in turn, blocking until a completed result becomes available.
    for (int i=0; i<runnables.length; ++i) {
      Future<Void> completed = cs.take();
    }
    

    【讨论】:

      【解决方案5】:

      Executor Framework 只适合您。
      这是伪代码:
      1. 创建执行器服务

      Executors type1Runnables = Executors.newFixedThreadPool(4);
      Executors type2Runnables = Executors.newFixedThreadPool(4);
      

      等等..
      2.向它提交任务

      for(){
      type1Runnables.submit(new Runnable1());
      type2Runnables.submit(new Runnable2);
      }
      

      3。调用执行者

      type1Runnables.invokeAll();
      type2Runnables.invokeAll();
      

      为了使其更通用,您也许可以编写自己的 executorservicefactory 来接受不同的可运行类型。

      【讨论】:

      • 此代码甚至无法编译:Executors 无法实例化。您的 for 循环不是有效的语法。 ExecutorService.invokeAll(...) 接受参数。这确实是一个非常懒惰和不清楚的答案,所以-1。
      猜你喜欢
      • 1970-01-01
      • 2014-12-10
      • 1970-01-01
      • 2012-08-14
      • 2017-01-23
      • 2015-04-08
      • 2014-06-08
      • 1970-01-01
      • 2018-10-27
      相关资源
      最近更新 更多