Re: Making one or more threads wait for another to produce a value
or fail
On Tue, 31 May 2011 16:46:55 +0100, Tom Anderson wrote:
On Tue, 31 May 2011, Peter Duniho wrote:
(Alternatively, you could provide a custom implementation of Future<V>
that doesn't wrap a Runnable like FutureTask<V>, letting your thread
continue even after delivering the Future<V>???but such an implementation
would be more complicated than just using the wait()/notifyAll()
pattern,
Not *that* much more complicated, i don't think. Both varieties of get
map on to calls to their corresponding variety of wait in a loop guarded
by a call to isDone, and isDone maps on to a check on whether the
verdict is delivered. cancel/isCancelled would require code over and
above the simplest wait/notify implentation, but not a lot.
The thing that bugs me is that if this is so simple, and as generally
useful as i think it is, why isn't it in the JDK already?
I don't know. But it is available in at least one other JVM language's
standard library: Clojure has functions called "promise" and "deliver"
for exactly this sort of scenario. Under the hood it combines a
CountDownLatch with a class instance variable to hold the result and
implements Clojure's IDeref interface. The Java equivalent would just be
some class Promise<T> with internal value and CountDownLatch and deliver
and get methods. Deliver would decrement the CountDownLatch and set the
value cell; get would see if the CountDownLatch was zero, block if it
wasn't, and return the value cell's contents if it was. It would also
throw InterruptedException and maybe have a version of get accepting a
timeout.
Something like:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class Promise<T> {
T value;
CountDownLatch cdl = new CountDownLatch(1);
public void deliver (T value) {
this.value = value;
cdl.countDown();
}
public T get () throws InterruptedException {
cdl.await();
return value;
}
public T get (long timeout, TimeUnit unit)
throws InterruptedException {
if (cdl.await(timeout, unit)) {
return value;
} else {
return null;
}
}
public T get (long timeout, TimeUnit unit, T to)
throws InterruptedException {
if (cdl.await(timeout, unit)) {
return value;
} else {
return to;
}
}
public T getEx (long timeout, TimeUnit unit)
throws InterruptedException {
if (cdl.await(timeout, unit)) {
return value;
} else {
throw new InterruptedException();
}
}
}
Untested and no Javadoc but it *should* work. In particular, the docu for
CountDownLatch says that value setting should "happen-before" value
getting. The first timeout-accepting get method will return null on
timeout. The second accepts a third parameter for the object to return on
timeout. The getEx method throws an InterruptedException if it times out.
I dedicate the above code into the public domain, so that it may
expeditiously find its way into JDK 8 perhaps sometime around 2040
without legal obstacles. :)