Suppose there are multiple threads trying to find a value, whichever thread finds it first should send the output to the main thread and all other threads should be terminated. Example -
public class WorkerThread implements Runnable {
@Override
public void run() {
// some long task here, returns int value
}
}
public class Main {
public static void main(String[] args){
// initialize multiple worker threads here
// then get result from the thread that completes first
}
}
I looked into docs and found invokeAny ExecutorService but this will return the result of any thread that has been completed successfully and not necessarily the first one.
CodePudding user response:
You can also use CountDownLatch and ExecutorService for achieving this.
Create CountDownLatch object with count = 1.
CountDownLatch latch = new CountDownLatch(1);
Use ExecutorService pool to execute the threads and pass the latch in all the threads.
workerThreadPool.execute(new WorkerThread(latch));
Wait for any thread to complete it's operation.
latch.await();
In the finally block of the thread run, shutdown the latch.
latch.countDown();
As soon as any thread countDown's the latch, the threadpool will stop all the other threads and shutdown.
workerThreadPool.shutdownNow();
The complete example would be below.
import static java.lang.Thread.sleep;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class WorkerThread implements Runnable
{
CountDownLatch _latch;
public WorkerThread(CountDownLatch latch)
{
_latch = latch;
}
@Override
public void run()
{
try
{
// some long task here, returns int value
System.out.println("In thread1 " this.toString());
sleep(5000);
}
catch (InterruptedException ex)
{
System.out.println("thread1 interupted");
}
finally
{
System.out.println("Finished1 " this.toString());
_latch.countDown();
}
}
}
class WorkerThread2 implements Runnable
{
CountDownLatch _latch;
public WorkerThread2(CountDownLatch latch)
{
_latch = latch;
}
@Override
public void run()
{
try
{
// some long task here, returns int value
System.out.println("In thread2 " this.toString());
sleep(10000);
}
catch (InterruptedException ex)
{
System.out.println("thread2 interupted");
}
finally
{
System.out.println("Finished2 " this.toString());
_latch.countDown();
}
}
}
public class Main
{
public static void main(String[] args) throws InterruptedException
{
ExecutorService workerThreadPool = Executors.newFixedThreadPool(2);
CountDownLatch latch = new CountDownLatch(1);
workerThreadPool.execute(new WorkerThread(latch));
workerThreadPool.execute(new WorkerThread2(latch));
latch.await();
workerThreadPool.shutdownNow();
}
}
CodePudding user response:
You -could- pass a reference to the Thread where the Thread can send its results. But you'd better follow advice in the other answers and use a better API for this :)
public static void main(//) {
ResultConsumer r = new ResultConsumer();
... create and start worker threads
}
public class WorkerThread implements Runnable {
public WorkerThread ( ResultConsumer r ) {
this.r=r
}
@Override
public void run() {
// some long task here, returns int value
r.sendResult(....)
}
}
CodePudding user response:
As @Andy Turner said, use a CompletionService
:
public static class WorkerThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int nextInt = new Random().nextInt(10000);
try {
System.out.println("I will cost " nextInt " ms to finish job.--" Thread.currentThread().getName());
Thread.sleep(nextInt);
} catch (InterruptedException ite) {
System.out.println("I am interrupted.--" Thread.currentThread().getName());
return -1;
}
System.out.println("I am finish.--" Thread.currentThread().getName());
return nextInt;
}
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
int nums = 3;
ExecutorService executorService = Executors.newFixedThreadPool(nums);
CompletionService<Integer> completionService = new ExecutorCompletionService<>(executorService);
while (nums-- > 0) {
completionService.submit(new WorkerThread());
}
Integer firstValue = completionService.take().get();
System.out.println("FirstValue is " firstValue);
executorService.shutdownNow();
}
And you can see in output, only one thread will complete the job (Because only call completionService#take
once
), other threads will be interrupted and exit
:
I will cost 8943 ms to finish job.--pool-1-thread-1
I will cost 9020 ms to finish job.--pool-1-thread-2
I will cost 5025 ms to finish job.--pool-1-thread-3
I am finish.--pool-1-thread-3
FirstValue is 5025
I am interrupted.--pool-1-thread-1
I am interrupted.--pool-1-thread-2