简体   繁体   English

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

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

I have a problem in Java where I want to spawn multiple concurrent threads simultaneously. 我在Java中有一个问题,我想同时产生多个并发线程。 I want to use the result of whichever thread/task finishes first, and abandon/ignore the results of the other threads/tasks. 我想使用第一个完成的线程/任务的结果,而放弃/忽略其他线程/任务的结果。 I found a similar question for just cancelling slower threads but thought that this new question was different enough to warrant an entirely new question. 我发现了一个类似的问题,只是取消了较慢的线程,但是我认为这个新问题足够不同,可以提出一个全新的问题。

Note that I have included an answer below based what I considered to be the best answer from this similar question but changed it to best fit this new (albeit similar) problem. 请注意,根据我认为是该类似问题的最佳答案,我在下面包括了一个答案,但将其更改为最适合此新问题(尽管类似)的答案。 I wanted to share the knowledge and see if there is a better way of solving this problem, hence the question and self-answer below. 我想分享知识,看看是否有更好的方法来解决此问题,因此下面有问题和自我解答。

You can use ExecutorService.invokeAny . 您可以使用ExecutorService.invokeAny From its documentation: 从其文档中:

Executes the given tasks, returning the result of one that has completed successfully …. 执行给定的任务,返回成功完成的任务的结果…。 Upon normal or exceptional return, tasks that have not completed are cancelled. 在正常或异常返回时,尚未完成的任务将被取消。

This answer is based off @lreeder's answer to the question " Java threads - close other threads when first thread completes ". 此答案基于@lreeder对“ Java线程-第一个线程完成时关闭其他线程 ”的回答。

Basically, the difference between my answer and his answer is that he closes the threads via a Semaphore and I just record the result of the fastest thread via an AtomicReference . 基本上,我的回答和他的回答之间的区别是,他通过Semaphore关闭了线程,而我只是通过AtomicReference记录了最快线程的结果。 Note that in my code, I do something a little weird. 请注意,在我的代码中,我做了一些奇怪的事情。 Namely, I use an instance of AtomicReference<Integer> instead of the simpler AtomicInteger . 即,我使用AtomicReference<Integer>的实例,而不是更简单的AtomicInteger I do this so that I can compare and set the value to a null integer; 我这样做是为了比较并设置为空整数; I can't use null integers with AtomicInteger . 我不能在AtomicInteger使用空整数。 This allows me to set any integer, not just a set of integers, excluding some sentinel value. 这使我可以设置任何整数,而不仅仅是一些整数,但不包括某些前哨值。 Also, there are a few less important details like the use of an ExecutorService instead of explicit threads, and the changing of how Worker.completed is set, because previously it was possible that more than one thread could finish first . 另外,还有一些不太重要的细节,例如使用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