简体   繁体   中英

Using Map of Futures, how do I notify() a single element within?

I'm attempting to hold a static list of Futures, and at a later time either cancel() or notify() the Futures which are in progress. The Callable class which is associated with these Futures has a wait() within it, so each one must be notified by an outside source to continue. However, my calls to notify() appear to be ignored, as the callables never get past their wait statement. The class with the list of Futures looks something like this:

private static Map <String, Future<Object>> results = new HashMap <String, Future<Object>>();

ExecutorService taskExecutor;

public void doStuff() {
    taskExecutor = Executors.newCachedThreadPool();

    // loop inifinitely - external processes will modify the conditions within
    while(!shutItDown) {

        if (<condition1>) {
            // condition 1 dictates the kick-off of a new callable
            Future<Object> future = taskExecutor.submit(new MyCallable(id));
            results.put(id, future);
        }
        else if (<condition2>) {
            // condition 2 represents a callable in a wait status needs
            // to be notified
            Future<Object> future = results.get(uid);
            if (future != null) {
                synchronized(future) {
                    future.notify();  // this doesn't have the desired effect!
                }
            }
        }
    }

}

The Callable class is just a mockup for now, looks similar to this:

public class MyCallable implements Callable<Object> {

private String id;

public MyCallable(String id) {
    this.id = id;
}

@Override
public Object call() throws Exception {     

    try {

        // do some work here, then wait on outside notification

        synchronized(this) {
            this.wait();  // never gets past here!!!
        }

        // do some other work here, once this has been notified
    } 
    catch (InterruptedException e) {
        e.printStackTrace();
    }

    return null;
}

The notify() method is called, but seems to have no effect. The object reference for the Future appears valid (ie the local variable "future" matches the reference of the future stored in the static list).

I'm probably missing some basic concept of concurrency here, but I expected that when condition2 is met, my Callable would proceed past the wait() call.

Note that if I use cancel() instead of notify(), it interrupts my runnable and causes an InterruptedException as I would expect.

You need to notify the exact same object. In your case you are notifying on the Future object but waiting on the MyCallable object. Unfortunately, I don't know of any easy way for your MyCallable object to see its wrapped Future so there is no wait for it to wait() on it.

One solution would be to pass in a lock object into your MyCallable constructor and then save it along with the associated Future . Something like:

  private static Map <String, FutureLock> results =
        new HashMap <String, FutureLock>();
  ...
  Object lock = new Object();
  Future<Object> future = taskExecutor.submit(new MyCallable(id, lock));
  results.put(id, new FutureLock(future, lock));
  ...

  public class FutureLock {
      private Future<Object> future;
      private Object lock;
      public FutureLock(Future<Object> future, Object lock) {
         this.future = future;
         this.lock = lock;
      }
      public void notify() {
         synchronized (lock) {
            lock.notify();
         }
      }
      public Object get() throws Exception {
         return future.get();
      }
  }

  public class MyCallable {
     private Object lock;
     public MyCallable(String id, Object lock) {
         this.lock = lock;
         ...
     }
  }

What you seem to be trying to achieve (though I may be wrong) is something like a SettableFuture .

Not sure if you need the computation ability of a Callable into an Executor, but the SettableFuture should be as easy as creating it and setting when you are ready while the other threads sit's there and waits for another thread to set.

There seems to be two routes you are approaching the problem.

One is with an Executor.

Executor e = Executors.newSingleThreadExecutor();
Future f = null;

Thread 1:

f = e.submit(new Callable(){
  public Object call(){
    return new Object();
  }
});

Thread 2:

f.get(); //will suspend Thread 2 until the Executor submits the callable

The other case is with a SettableFuture

final SettableFuture f = new SettableFuture();

Thread 1:

f.set(new Object());

Thread 2:

f.get(); //will suspend Thread 2 until Thread 1 set's the Future.

Both would achieve the same type of waiting mechanics the differences are that Thread 1 in the first example will submit to a separate thread to create the object. Thread 2 will still wait until the other thread has completed the operation. The second example will have thread 2 waiting until thread 1 completes and set's the Future.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM