0

我正在装饰一个类,其中一些方法返回一个 Future。在一种特定情况下,我对 Future 的结果感兴趣,并想在通知调用者之前拦截它。也许是这样的:

public Future<Integer> getCalculation() {
  Future<Integer> future = realObject.getCalculation();
  // Get the result, store it somewhere
  // ???
}

在 getCalculation 是同步的情况下,我可以调用realObject,等待响应,对它做任何我想做的事情,然后简单地返回它,如下所示:

public int getCalculation() {
  int result = realObject.getCalculation();
  this.cache = result;
  return result;
}

如何将此模式应用于返回 Future 的东西?

4

5 回答 5

2

SettableFuture来自 Guava 库的可能会派上用场: http ://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/util/concurrent/SettableFuture.html

您确实可以创建一个SettableFuture并为其分配您的直接价值。

于 2013-10-02T14:09:30.743 回答
1

如何将此模式应用于返回 Future 的东西?

我认为您必须在这里实现自己的代理Future类。可能类似于以下委托给代理的内容:

public class FutureProxy implements Future<Integer> {
    private Calculator calculator;
    private Future<Integer> delegate;
    public FutureProxy(Calculator calculator, Future<Integer> delegate) {
        this.calculator = calculator;
        this.delegate = delegate;
    }
    // proxy all of the methods
    public boolean isDone() {
        return delegate.isDone();
    }
    ...
    // for get, you can then call back to `calculator`
    public Integer get() throws InterruptedException, ExecutionException {
        Integer result = delegate.get();
        calculator.setCachedCalculation(result);
        return result;
    }
    // need to handle get(long, TimeUnit) as well
    public Integer get(long timeout, TimeUnit unit)
          throws InterruptedException, ExecutionException, TimeoutException;
        Integer result = delegate.get(timeout, unit);
        calculator.setCachedCalculation(result);
        return result;
    }
}

然后你的装饰吸气剂看起来像:

public Future<Integer> getCalculation() {
   Future<Integer> future = realObject.getCalculation();
   return new FutureProxy(this, future);
}

正如您在评论中提到的,如果FutureProxy是装饰器的内部类,则Calculator不会被注入。如果代理正在更新将由其他线程访问的volatile字段,请确保您使用字段。Calculator

于 2013-10-01T18:53:25.593 回答
0

您将要使用从装饰器返回的代理 Future。然后,您将需要实现代理 Future 的 #get 方法来阻止,直到真正的 Future 返回,并且您已经能够对结果执行您的工作。

class ProxyFuture<T> implements Future<T> {

    private Future<T> original;
    private Task task;
    ExecutorService executor;

    public ProxyFuture(Future<T> original, Runnable task, ExecutorService executor) {
        this.original = original;
        this.task = task;
        this.executor = executor;
    }

    public T get() throws InterruptedException, ExecutionException {
        T result = original.get();
        executor.execute(task);
        return result;
    }


}

在这里,您有一些用于 ExecutorService 的应用程序资源,并让您的 Decorator 创建一个 Runnable,然后将其传递给新的 Future。这样,您的 ProxyFuture 将在将结果传递回原始请求者之前调用装饰器中的代码。

以下是一个示例装饰器

public class Decorator {

    Object original;

    public Decorator(Object original){
        this.original = original;
    }

    public void doDecoratedBehavior(){

    }

    public Runnable createTask(){
        return new Runnable(){
            public void run(){
                doDecoratedBehavior();
            }
        };
    }
}
于 2013-10-01T18:44:46.293 回答
0

在这种情况下,您的方法必须阻止...

public Future<Integer> getCalculation() {
  Future<Integer> future = realObject.getCalculation();

  // Get the result, store it somewhere
  //the .get() method is blocking...
  myInteger = future.get();

  return future;
}

调用者代码将立即成功获取()结果......

编辑

如果需要非阻塞,请为将来创建一个包装类

FutureWrapper<T> implements Future<T> {

    Future<T> wrapped;
    public FutureWrapper(Future<T> toBeWrapped) {
        wrapped = toBeWrapped;
    }

    public T get() {
        return wrapped.get();
    }

    //the other wrapper methods left out for sake of clarity
}


MyWrapper extends FutureWrapper<Integer>{

    //100% boilerplate
    public MyWrapper(Future<Integer> toBeWrapped) {
        super(toBeWrapped);
    }

    @Override
    public Integer get() {
        Integer value = super.get();
        //do the dirty stuff
        return value;
    }

}

编辑2

如果“肮脏的东西”涉及抚摸装饰器的隐私(对不起,不是母语英语:)(双关语)),您可以在装饰器类中定义 MyWrapper 类:

public MyDecorator {

    Integer dirtyStuff;


    public class MyWrapper extends FutureWrapper<Integer>{
        //boilerplate omitted
        public Integer get() {
            dirtyStuff= super.get();
            return dirtyStuff;
        }
}
于 2013-10-01T18:48:08.047 回答
0

根据您的描述,您的异步是毫无意义的。这是我对您所要求的内容的伪代码解释-

Future<Integer> getCalculation(){
    Future<Integer> future = realObject.getCalculation();
    //asynch code runs while you presumably do other stuff.
    ....
    ....
    result = future.get();
    return future;
}

但在这一点上,使用未来是没有意义的。异步不会为您节省时间。事实上,由于额外的开销,它实际上使事情花费了更长的时间。这是因为当您result = future.get()等待线程完成时,因此会丢失您节省的所有时间。这也意味着返回未来没有任何优势,因为它不能做更多的事情result

换句话说,这段代码会更快,也更少麻烦(假设你在计算过程中不需要做任何事情) -

Integer getCalculation(){
    return realObject.getCalculation();//assume it is rewritten to just return an int.
}
于 2013-10-01T19:20:02.633 回答