Wrapping an asynchronous computation into a synchronous (blocking) computation
Using your own Future implemenation:
public class BazComputationFuture implements Future<Baz>, BazComputationSink {
private volatile Baz result = null;
private volatile boolean cancelled = false;
private final CountDownLatch countDownLatch;
public BazComputationFuture() {
countDownLatch = new CountDownLatch(1);
}
@Override
public boolean cancel(final boolean mayInterruptIfRunning) {
if (isDone()) {
return false;
} else {
countDownLatch.countDown();
cancelled = true;
return !isDone();
}
}
@Override
public Baz get() throws InterruptedException, ExecutionException {
countDownLatch.await();
return result;
}
@Override
public Baz get(final long timeout, final TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
countDownLatch.await(timeout, unit);
return result;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public boolean isDone() {
return countDownLatch.getCount() == 0;
}
public void onBazResult(final Baz result) {
this.result = result;
countDownLatch.countDown();
}
}
public Future<Baz> doSomething(Foo fooArg, Bar barArg) {
BazComputationFuture future = new BazComputationFuture();
doSomethingAsync(fooArg, barArg, future);
return future;
}
public Baz doSomethingAndBlock(Foo fooArg, Bar barArg) {
return doSomething(fooArg, barArg).get();
}
The solution creates a CountDownLatch internally which is cleared once the callback is received. If the user calls get, the CountDownLatch is used to block the calling thread until the computation completes and call the onBazResult callback. The CountDownLatch will assure that if the callback occurs before get() is called the get() method will return immediately with a result.
Well, there is the simple solution of doing something like:
public Baz doSomethingAndBlock(Foo fooArg, Bar barArg) {
final AtomicReference<Baz> notifier = new AtomicReference();
doSomethingAsync(fooArg, barArg, new BazComputationSink() {
public void onBazResult(Baz result) {
synchronized (notifier) {
notifier.set(result);
notifier.notify();
}
}
});
synchronized (notifier) {
while (notifier.get() == null)
notifier.wait();
}
return notifier.get();
}
Of course, this assumes that your Baz
result will never be null…