繁体   English   中英

Java:如何使用多个已完成线程中第一个线程的结果?

[英]Java: How do I use the result of the first of multiple threads that complete?

我在Java中有一个问题,我想同时产生多个并发线程。 我想使用第一个完成的线程/任务的结果,而放弃/忽略其他线程/任务的结果。 我发现了一个类似的问题,只是取消了较慢的线程,但是我认为这个新问题足够不同,可以提出一个全新的问题。

请注意,根据我认为是该类似问题的最佳答案,我在下面包括了一个答案,但将其更改为最适合此新问题(尽管类似)的答案。 我想分享知识,看看是否有更好的方法来解决此问题,因此下面有问题和自我解答。

您可以使用ExecutorService.invokeAny 从其文档中:

执行给定的任务,返回成功完成的任务的结果…。 在正常或异常返回时,尚未完成的任务将被取消。

此答案基于@lreeder对“ Java线程-第一个线程完成时关闭其他线程 ”的回答。

基本上,我的回答和他的回答之间的区别是,他通过Semaphore关闭了线程,而我只是通过AtomicReference记录了最快线程的结果。 请注意,在我的代码中,我做了一些奇怪的事情。 即,我使用AtomicReference<Integer>的实例,而不是更简单的AtomicInteger 我这样做是为了比较并设置为空整数; 我不能在AtomicInteger使用空整数。 这使我可以设置任何整数,而不仅仅是一些整数,但不包括某些前哨值。 另外,还有一些不太重要的细节,例如使用ExecutorService代替显式线程,以及更改Worker.completed方式,因为以前可能有多个线程可以先完成

public class ThreadController {
  public static void main(String[] args) throws Exception {
    new ThreadController().threadController();
  }

  public void threadController() throws Exception {
    int numWorkers = 100;

    List<Worker> workerList = new ArrayList<>(numWorkers);
    CountDownLatch startSignal = new CountDownLatch(1);
    CountDownLatch doneSignal = new CountDownLatch(1);
    //Semaphore prevents only one thread from completing
    //before they are counted
    AtomicReference<Integer> firstInt = new AtomicReference<Integer>();
    ExecutorService execSvc = Executors.newFixedThreadPool(numWorkers);

    for (int i = 0; i < numWorkers; i++) {
      Worker worker = new Worker(i, startSignal, doneSignal, firstInt);
      execSvc.submit(worker);
      workerList.add(worker);
    }

    //tell workers they can start
    startSignal.countDown();

    //wait for one thread to complete.
    doneSignal.await();

    //Look at all workers and find which one is done
    for (int i = 0; i < numWorkers; i++) {
      if (workerList.get(i).isCompleted()) {
        System.out.printf("Thread %d finished first, firstInt=%d\n", i, firstInt.get());
      }
    }
  }
}

class Worker implements Runnable {

  private final CountDownLatch startSignal;
  private final CountDownLatch doneSignal;
  // null when not yet set, not so for AtomicInteger
  private final AtomicReference<Integer> singleResult;
  private final int id;
  private boolean completed = false;

  public Worker(int id, CountDownLatch startSignal, CountDownLatch doneSignal, AtomicReference<Integer> singleResult) {
    this.id = id;
    this.startSignal = startSignal;
    this.doneSignal = doneSignal;
    this.singleResult = singleResult;
  }

  public boolean isCompleted() {
    return completed;
  }

  @Override
  public void run() {
    try {
      //block until controller counts down the latch
      startSignal.await();
      //simulate real work
      Thread.sleep((long) (Math.random() * 1000));

      //try to get the semaphore. Since there is only
      //one permit, the first worker to finish gets it,
      //and the rest will block.
      boolean finishedFirst = singleResult.compareAndSet(null, id);
      // only set this if the result was successfully set
      if (finishedFirst) {
        //Use a completed flag instead of Thread.isAlive because
        //even though countDown is the last thing in the run method,
        //the run method may not have before the time the 
        //controlling thread can check isAlive status
        completed = true;
      }
    }
    catch (InterruptedException e) {
      //don't care about this
    }
    //tell controller we are finished, if already there, do nothing
    doneSignal.countDown();
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM