10

Suppose I have a blocking method called check as follows:

boolean check(String input) {}

which will do some check against the input and return the decision.

Now I want to run this check against a list of inputs asynchronously, and I want to return to the main thread right after one of the inputs passing the check, so I don't have to wait for all asynchronous call to be completed. The only one scenario for waiting all threads to complete is that there are no inputs passing the check. Running the method asynchronously with list of inputs is straightforward but I'm not sure how to return to the main thread after getting the target output for the input passing the check.

Jiahui Zhang
  • 538
  • 1
  • 5
  • 12

3 Answers3

5

Here is a really simple working example to achieve what you are asking for

Future<Boolean> future = CompletableFuture.runAsync(() -> {
    // Do your checks, if true, just return this future
    System.out.println("I'll run in a separate thread than the main thread.");
});

// Now, you may want to block the main thread waiting for the result
while(!future.isDone()) {
    // Meanwhile, you can do others stuff
    System.out.println("Doing other stuff or simply waiting...");
}

// When future.isDone() returns, you will be able to retrieve the result
Boolean result = future.get();
Snix
  • 541
  • 4
  • 12
4

A basic parallelStream will do exactly that:

boolean match = inputs.parallelStream().anyMatch(input -> check(input));

Returns early with match==true, iff some input is found that matches check. match will be false if all inputs are checked and none matched.

In the standard case it will use the fork/join thread pool. But with some further effort you can avoid that.

k5_
  • 5,450
  • 2
  • 19
  • 27
  • An operation on a ParallelStream **is still blocking** and will wait for all the threads it spawned to finish. These threads are executed asynchronously (they don't wait for a previous one to finish), but that doesn't mean your whole code starts behaving asynchronously. Source: [here](https://stackoverflow.com/questions/44945521/java-8-parallel-stream-blockingcode-possible) – Snix Jun 09 '20 at 23:22
  • 1
    See also [Java 8 parallel stream + anyMatch - do threads get interrupted once a match is found?](https://stackoverflow.com/q/27235839/5698098). – Ivo Mori Jun 10 '20 at 00:09
  • Yes, the thread calling `anyMatch` is blocked. If you don't want that. Snix approach is the way to go. Polling the Future (with `isDone()`) or blocking (with `get()`) when all intermediate work is done. You can also use both together. Start the Future, and use a `parallelStream()` in the future to use multiple threads to check the input. – k5_ Jun 10 '20 at 00:14
0

Take a look at this example and see if it helps you:

package callrandom;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

import static java.lang.System.exit;
import static java.lang.System.out;
import static java.util.concurrent.Executors.newFixedThreadPool;
import static java.util.logging.Level.SEVERE;
import static java.util.logging.Logger.getLogger;

import static callrandom.Randomize.gotFive;

public class CallRandom {

    public static void main(String[] args){
        int MAXTHREADS = 5;
        List<Future<Integer>> futs = new ArrayList<>();

        ExecutorService executor = newFixedThreadPool(MAXTHREADS);
        ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;

        for (int x = 0; x < MAXTHREADS; x++) {
            out.println("Run instance: " + x);
            futs.add(executor.submit(new Randomize()));
        }

        do {
        } while (!gotFive);

        for (int x = 0; x < MAXTHREADS; x++) {
            if (futs.get(x).isDone()) {
                try {
                    out.println("Return from thread: " + x + " = " + futs.get(x).get());
                } catch (InterruptedException | ExecutionException ex) {
                    getLogger(CallRandom.class.getName()).log(SEVERE, null, ex);
                    throw new RuntimeException(ex);
                }
            }
            out.println("Cancel instance: " + x + " = " + futs.get(x).cancel(true));
        }
        exit(0);
    }
}

...and the thread:

package callrandom;

import static java.lang.System.in;
import static java.lang.System.out;
import static java.lang.Thread.currentThread;
import java.util.Scanner;
import java.util.concurrent.Callable;

public class Randomize implements Callable<Integer> {

    public static volatile Boolean gotFive = false;

    @Override
    public Integer call() throws Exception {
        return findNumber();
    }

    private Integer findNumber() throws InterruptedException {
        int number = 0;
        do {
            Scanner sc = new Scanner(in);
            out.println(currentThread().getName() + ": Insert a number [5 stops this threads and triggers main thread to interrupt all others]:");
            number = sc.nextInt();
        } while (number != 5 && !gotFive);
        gotNumber();
        return number;
    }

    public synchronized void gotNumber() {
        out.println(currentThread().getName() + " got a 5!");
        gotFive = true;
    }
}

You have 5 threads running and waiting for input. They keep asking for input until you enter the number 5 in any one of them. That thread returns to main thread and the main thread cancels all remaining threads. You may have to complete a cycle, meaning inserting at least 1 number in each thread, for the thread to acknowledge the interrupt request and cancel itself, although there are times when it cancels itself without completing the cycle.

A critical appreciation of this example would certainly be appreciated since this is the first time I use Callable and Future.

Dan M
  • 4,340
  • 8
  • 20