Search code examples
javathreadpoollazy-evaluationjava.util.concurrentshort-circuiting

How to perform short-circuit evaluation in Java on two parallel threads that return boolean values?


I'm looking for guidance for a problem logically equivalent to the following:

public boolean parallelOR() {
    ExecutorService executor = Executors.newFixedThreadPool(2);
    Future<Boolean> taskA = executor.submit( new SlowTaskA() );
    Future<Boolean> taskB = executor.submit( new SlowTaskB() );

    return taskA.get() || taskB.get(); // This is not what I want
    // Exception handling omitted for clarity
 }

The above construction gives the correct result but always waits for taskA to complete even if the result is already known since taskB has completed.

Is there a better construction which will allow a value to be returned if either of the threads returns true without waiting for the second thread to complete?

(The platform involved is Android, if that affects the result).


Solution

  • Here's an implementation of ParallelOr using ExecutorCompletionService. It waits on tasks until one returns true. If none do, it eventually returns false.

    public class ParallelOr {
    
        static class LongTask implements Callable<Boolean> {
    
            private int milliseconds;
            private boolean val;
    
            public LongTask(int milliseconds, boolean val) {
                this.milliseconds = milliseconds;
                this.val = val;
            }
    
            @Override
            public Boolean call() throws Exception {
                try {
                    Thread.sleep(milliseconds);
                } catch(Exception ex) {}
                return val;
            }
        }
    
        static boolean ParallelOr(List<Callable<Boolean>> tasks) {
            ExecutorService pool = Executors.newFixedThreadPool(tasks.size());
            ExecutorCompletionService<Boolean> completionService
                    = new ExecutorCompletionService<Boolean>(pool);
    
            for(Callable<Boolean> task : tasks) {
                completionService.submit(task);
            }
    
            for(int i = 0; i < tasks.size(); i++) {
                try {
                    Future<Boolean> result = completionService.take();
                    if(result.get()) {
                        return true;
                    }
                } catch (InterruptedException e) {
                } catch (ExecutionException e) {}
            }
    
            return false;
        }
    
    
        public static void main(String[] args) {
            ArrayList<Callable<Boolean>> tasks = new ArrayList<Callable<Boolean>>();
    
            tasks.add(new LongTask(1000, true));
            tasks.add(new LongTask(500, false));
            tasks.add(new LongTask(6000, false));
    
            boolean result = ParallelOr(tasks);
    
            System.out.println(result);
        }
    }
    

    Thanks to @Lav for pointing out the ExecutorCompleteionService class.