簡體   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