我在Java中遇到问题,我想同时生成多个并发线程。我想先使用任何线程/任务完成的结果,然后放弃/忽略其他线程/任务的结果。我在cancelling slower threads找到了类似的问题,但认为这个新问题不同,足以保证一个全新的问题。
请注意,我已根据我认为这个类似问题的最佳答案包含了以下答案,但将其更改为最适合这个新问题(虽然类似)。我想分享知识,看看是否有更好的方法来解决这个问题,因此下面的问题和自我回答。
答案 0 :(得分:2)
您可以使用ExecutorService.invokeAny。从其文档:
执行给定的任务,返回已成功完成的任务的结果....正常或特殊退货后,未完成的任务将被取消。
答案 1 :(得分:0)
这个答案是基于@lreeder对问题“Java threads - close other threads when first thread completes”的回答。
基本上,我的回答与他的答案之间的区别在于他通过Semaphore关闭线程,我只是通过AtomicReference记录最快线程的结果。请注意,在我的代码中,我做了一些有点奇怪的事情。也就是说,我使用AtomicReference<Integer>
的实例而不是更简单的AtomicInteger
。我这样做,以便我可以比较并将值设置为空整数;我不能使用AtomicInteger
的空整数。这允许我设置任何整数,而不仅仅是一组整数,不包括一些sentinel值。此外,还有一些不那么重要的细节,例如使用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();
}
}