使用期货地图,如何通知()单个元素?

时间:2012-06-04 18:41:12

标签: java concurrency notify future

我试图持有一个静态的期货清单,并在以后取消()或通知()正在进行的期货。与这些期货相关联的Callable类在其中有一个wait(),因此每个必须由外部来源通知才能继续。但是,我对notify()的调用似乎被忽略了,因为callables永远不会超过他们的wait语句。具有Futures列表的类看起来像这样:

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!
                }
            }
        }
    }

}

Callable类现在只是一个模型,看起来类似于:

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;
}

调用notify()方法,但似乎没有效果。 Future的对象引用看似有效(即局部变量“future”与存储在静态列表中的未来引用相匹配)。

我可能在这里遗漏了一些基本的并发概念,但我预计当满足condition2时,我的Callable将继续通过wait()调用。

请注意,如果我使用cancel()而不是notify(),它会中断我的runnable并导致InterruptedException,正如我所料。

2 个答案:

答案 0 :(得分:1)

您需要notify 完全相同的对象。在您的情况下,您正在通知Future对象,但正在等待MyCallable对象。不幸的是,我不知道你的MyCallable对象有什么简单的方法可以看到它被包裹Future所以没有等待wait()就可以了。

一种解决方案是将锁定对象传入MyCallable构造函数,然后将其与关联的Future一起保存。类似的东西:

  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;
         ...
     }
  }

答案 1 :(得分:0)

你似乎想要实现的目标(尽管我可能错了)类似于SettableFuture

不确定你是否需要Callable到Executor的计算能力,但是SettableFuture应该像创建它一样简单,当你准备就绪时设置,而其他线程就在那里并等待另一个线程设置。

您似乎有两条路线正在接近问题。

一个是执行者。

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

主题1:

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

主题2:

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

另一种情况是使用SettableFuture

final SettableFuture f = new SettableFuture();

主题1:

f.set(new Object());

主题2:

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

两者都会实现相同类型的等待机制,区别在于第一个示例中的线程1将提交给单独的线程来创建对象。线程2仍将等待,直到另一个线程完成操作。第二个例子将让线程2等到线程1完成并设置为Future。