3

First of all, I've decided to make my class blocking (to make it easier for the consumer to use - but maybe more tedious for me to write). As opposed to having the consumer define asynchronous callbacks. Is this a good design pattern? This way, a user can get expected behaviour, but implement their own multi-threading if they're dissatisfied with how long the thread's blocked for.

I've got a constructor that sets a final field in a class, based on the result of an async callback:

class Example {
    private final int x;

    Example(){
        asyncFunc(/* callback */ result -> x = result)
    }
}

This doesn't work, so I've used atomic references, and implemented a blocking loop, until the result it returned, like so:

class Example {
    private final int x;

    Example(){
        x = waitAsyncFunc();
    }

    private int waitAsyncFunc(){
        AtomicBoolean finished = new AtomicBoolean(false);
        AtomicReference<byte[]> result = new AtomicReference<>();
        asyncFunc(result -> {
            result .set(res);
            finished.set(true);
        });
        while (!finished.get()) { /* No op */ }
        return result.get();
    }

}

Is this a good way to block / retrieve the result?

Holger
  • 285,553
  • 42
  • 434
  • 765
Tobiq
  • 2,489
  • 19
  • 38
  • Instead of using the AtomicBoolean for waiting for the thread's completion you could use a CountDownLatch (https://stackoverflow.com/questions/17827022/how-is-countdownlatch-used-in-java-multithreading). Basically you define a latch with how often it has to be counted down. With _await_ you can pause your thread until the latch has been counted down to 0. – lugiorgi Feb 12 '20 at 12:41
  • why you have to wait the result inside class Example? – You're awesome Feb 12 '20 at 12:46
  • Just an example @You'reawesome. In my actual class, both (the blocking and asynchronous) methods are inside my class – Tobiq Feb 12 '20 at 12:49
  • Generally, I don't think reassign a variable inside lambda is a good idea. I would create another asyn function to take care of result returned by `asyncFunc` – You're awesome Feb 12 '20 at 13:25
  • @You'reawesome I dont understand what you mean – Tobiq Feb 12 '20 at 13:26
  • If you like answer of @lugiorgi you may want to accept it – HoRn Feb 12 '20 at 15:50
  • @HoRn I like to wait for alternative answers (like holgers, which I actually like more) before accepting – Tobiq Feb 12 '20 at 19:59

2 Answers2

4

The simplest solution would be

class Example {
    private final int x;

    Example() {
        CompletableFuture<Integer> f = new CompletableFuture();
        asyncFunc(f::complete);
        x = f.join();
    }
}

But consider the alternative of waiting for the asynchronous job’s completion before even constructing the Example instance.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • Perfection. With your suggestion, do you mean pass the async result into the constructor as a parameter? This would be duplicated for every instantiation of the class - convoluting the use of the class – Tobiq Feb 12 '20 at 20:05
  • 1
    This can be avoided with factory methods. Just a suggestion. I tend to avoid blocking operations in constructors. – Holger Feb 13 '20 at 08:18
3

Instead of blocking the thread with a loop you can use a CountDownLatch.

As taken from the docs (https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html)

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

Your code would look something like

private int waitAsyncFunc() throws InterruptedException {
        CountDownLatch latch = new new CountDownLatch(1);
        AtomicReference<byte[]> result = new AtomicReference<>();
        asyncFunc(result -> {
            result.set(res);
            latch.countDown();
        });
        latch.await(); //You can even specify a timeout
        return result.get();
    }
lugiorgi
  • 473
  • 1
  • 4
  • 12