Search code examples
javaasynchronousguava

How does Java guarantee callback is called when future.isDone() in ListenableFuture


when going through the ListenableFutre interface, it notes in the doc that

addListener()

Registers a listener to be run on the given executor. The listener will run when the Future's computation is complete or, if the computation is already complete, immediately.`

Since Future.get() is a blocking call, how does Java guarantee certain future is Done? are they spinning on this? I understand that with Framework like dagger producers, it is kinda easy to understand (once task is done, write to something, the monitoring thread will be notified). in ListenableFuture case, does jvm support something like this out of box?

using wait()/notify() like mechanism ?


FollowUp Question: as all of you put, it is the caller actually guarantee the listener to be run, normal case to use a ListenableFuture would be ListenableFuture future = Caller.call(), with caller and callee in different threads or even in different JVMs, how is this done in java? the listener in stored in both the caller thread and callee thread ? or using remote reigstery when in differnt JVMs?


Solution

  • As already mentioned, the best way to understand the ListenableFuture is to look how it is implemented. When you call addListener(Runnable listener, Executor exec), you provide a Runnable listener and an Executor to run this listener, so it is you who decides how your listener is executed.

    the listener is stored in both the caller thread and callee thread ?

    The listener is stored inside the future, in the ExecutionList:

    // The execution list to hold our executors.
    private final ExecutionList executionList = new ExecutionList();
    

    And addListener(Runnable listener, Executor exec) does just following:

    public void addListener(Runnable listener, Executor exec) {
        executionList.add(listener, exec);
    }
    

    So when the future completes, it calls the set(V value) method:

    protected boolean set(@Nullable V value) {
        boolean result = sync.set(value);
        if (result) {
          executionList.execute();
        }
        return result;
    }
    

    and all listeners are executed like this: executor.execute(runnable);