similar questions:
I have an object with a method I would like to expose to library clients (especially scripting clients) as something like:
interface MyNiceInterface { public Baz doSomethingAndBlock(Foo fooArg, Bar barArg); public Future<Baz> doSomething(Foo fooArg, Bar barArg); // doSomethingAndBlock is the straightforward way; // doSomething has more control but deals with // a Future and that might be too much hassle for // scripting clients }
but the primitive "stuff" I have available is a set of event-driven classes:
interface BazComputationSink { public void onBazResult(Baz result); } class ImplementingThing { public void doSomethingAsync(Foo fooArg, Bar barArg, BazComputationSink sink); }
where ImplementingThing takes inputs, does some arcane stuff like enqueueing things on a task queue, and then later when a result occurs, sink.onBazResult()
gets called on a thread that may or may not be the same thread as ImplementingThing.doSomethingAsync() was called.
Is there a way I can use the event-driven functions I have, along with concurrency primitives, to implement MyNiceInterface so scripting clients can happily wait on a blocking thread?
edit: can I use FutureTask for this?
In my discussion of “async over sync,” I strongly suggested that if you have an API which internally is implemented synchronously, you should not expose an asynchronous counterpart that simply wraps the synchronous method in Task. Run.
Yes, We can call a synchronous method inside a future method. Both will run on Asynchronous Only.
Using Future to convert Async to Sync method call Future is an Interface which represents the result of an asynchronous computation. We can use Future. get() or Future. get(long timeout, TimeUnit unit) to get result as a sync method call.
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…
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With