0

Im looking for a way to synchronize multiple asynchronous operations. I'd like to use a BlockingQueue with a size equal to my operations but who can i wait till the Queue is full?

Im looking for something like a reversed Blocking Queue.

I need to gather the Results of each Thread at the End. The AsyncHandler is fixed, its already a ThreadExecutor underlying, i cannot start new Threads.

//3 Times
makeAsync(new AsyncHandler() {
   onSuccess() {
     ..
     queue.put(result)
   }

   onFailure() {
     ..
   }
});

//Blocking till the Queue is full
List<Results> = queue.takeAll()

Bonus Question: I need a way to end the wait when one of my Requests fails

Michele
  • 6,126
  • 2
  • 41
  • 45

5 Answers5

2

I've never had need to do this sort of thing, but you might have some luck using a CountDownLatch or CyclicBarrier from your various threads.

Powerlord
  • 87,612
  • 17
  • 125
  • 175
  • Thanks for the Answer, those Tools would fit perfect but i need to collect the results when all Tasks are done. – Michele Aug 07 '14 at 21:32
1

If you're using Java 8, do the following:

  • With each call to makeAsync, create a CompletableFuture<Result> instance and make it available to the AsyncHandler, and have the caller keep a reference too, say in a list.
  • When an async task completes normally, have it call complete(result) on its CompletableFuture instance.
  • When an async task completes with an error, have it call completeExceptionally(exception) on its CompletableFuture instance.
  • After initiating all the asynchronous tasks, have the caller call CompletableFuture.allOf(cfArray).join(). Unfortunately this takes an array, not a list, so you have to convert. The join() call will throw an exception if any one of the tasks completed with an error. Otherwise, you can collect the results from the individual CompletableFuture instances by calling their get() methods.

If you don't have Java 8, you'll have to sort of roll your own mechanism. Initialize a CountDownLatch to the number of async tasks you're going to fire off. Have each async task store its result (or an exception, or some other means of indicating failure) into a thread-safe data structure and then decrement ('countDown`) the latch. Have the caller wait for the latch to reach zero and then collect the results and errors. This isn't terribly difficult, but you have to determine a means for storing valid results as well as recording whether an error occurred, and also maintain a count manually.

Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
1

What you describe with

//Blocking till the Queue is full
List<Results> results = queue.takeAll();

does not differ semantically from “take as much items as the queue’s capacity”. If you know the capacity you can achieve this by:

// preferably a constant which you also use to construct the bounded queue
int capacity;

List<Results> results = new ArrayList<>(capacity);
queue.drainTo(results, capacity);
while(result.size()<capacity)
    queue.drainTo(results, capacity-result.size());

This will block until it has received as much items as the capacity which is, as said, the same as waiting for the queue to become full (has a size equal to its capacity) and than take all items. The only difference is that the event of the queue becoming full is not guaranteed to happen, e.g. if you intend your async operations to offer items until the queue is full, it does not work this way.

If you don’t know the capacity, you are out of luck. There is not even a guaranty that an arbitrary BlockingQueue is bounded, read, it might have an unlimited capacity.

On the other hand, if the asynchronous operations are able to detect when they have finished, they could simply collect the items in a list locally and put the entire list into a BlockingQueue<List<Results>> as a single item once they are done. Then your code waiting for it needs only a single take to get the entire list.

Holger
  • 285,553
  • 42
  • 434
  • 765
0

If you can modify methodAsync(), then it's as simple as to use a CountDownLatch after each time you put some elements in the queue and have the main thread wait for such a CountDownLatch.

If unfortunately you cannot modify methodAsync(), then simply wrap the queue and give it a count down latch, and then override the add() method to count down this latch. The main method just wait it to be done.

Having said the above, your program structure smells not well organized.

Alex Suo
  • 2,977
  • 1
  • 14
  • 22
0

I wrote a solution to your problem. I used an additional BlockingQueue with "freeWorkers" with the same capacity as the real worker thread pool. I just take the "freeWorker" before starting the next task and each task returns the "freeWorker" to the "freeWorkes" queue. Here is an example:

package poc.concurrecy;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static java.util.concurrent.TimeUnit.SECONDS;

public class ThreadPoolQueueTest {

    public static final String WHITE = "\033[0;39m";
    public static final Random RANDOM = new Random();

    public static void main(String[] args) throws InterruptedException {
        int capacity = 5;
        LinkedBlockingQueue<Integer> freeWorkers = new LinkedBlockingQueue<>(capacity);
        for (int i = 1; i < capacity + 1; i++) freeWorkers.put(i);
        System.out.println(WHITE + freeWorkers);
        System.out.println(WHITE + "===========");
        LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(capacity);
        ExecutorService executor =
                new ThreadPoolExecutor(capacity, capacity,
                        0L, TimeUnit.MILLISECONDS,
                        workQueue);
        try {
            for (int i = 0; i < 10; i++) {
                Integer freeWorker = freeWorkers.take();
                String color = "\033[0;3" + freeWorker + "m";
                System.out.println(WHITE + "take free worker " + freeWorker);
                System.out.println(WHITE + freeWorkers);
                executor.execute(() -> {
                    System.out.println(color + "run " + freeWorker);
                    delay(1 + RANDOM.nextInt(3));
                    System.out.println(color + "done " + freeWorker);
                    try {
                        System.out.println(color + "return free worker " + freeWorker);
                        freeWorkers.put(freeWorker);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                });
                System.out.println(WHITE + "put task " + freeWorker);
            }

        } finally {
            executor.shutdown();
            executor.awaitTermination(5, TimeUnit.SECONDS);
        }
        System.out.println(WHITE + "==========");
        System.out.println(WHITE + freeWorkers);
    }

    public static void delay(int timeout) {
        try {
            SECONDS.sleep(timeout);
        } catch (InterruptedException e) {
            // do nothing
        }
    }
}

here is an output:

[1, 2, 3, 4, 5]
===========
take free worker 1

[2, 3, 4, 5]
put task 1
take free worker 2
[3, 4, 5]
put task 2
take free worker 3
[4, 5]
put task 3
take free worker 4
[5]
put task 4
take free worker 5
[]
put task 5
run 3
run 5
run 1
run 2
run 4
done 4
return free worker 4
take free worker 4
[]
put task 4
run 4
done 3
return free worker 3
take free worker 3
[]
put task 3
run 3
done 5
return free worker 5
done 1
return free worker 1
take free worker 5
[1]
put task 5
take free worker 1
run 5
[]
put task 1
run 1
done 3
return free worker 3
done 2
return free worker 2
take free worker 3
[2]
put task 3
run 3
done 5
return free worker 5
done 1
return free worker 1
done 4
return free worker 4
done 3
return free worker 3
==========
[2, 5, 1, 4, 3]

Obviously you can simplify this code and remove all debug infos "System.out"