[英]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.