添加/扩展由ListeningExecutorService创建的Future的行为

时间:2012-01-19 18:19:10

标签: java guava

最终目标是根据Callable / Runnable参数的类型向ListenableFuture s 添加额外行为。我想为每个Future方法添加额外的行为。 (示例用例可以在AbstractExecutorService's javadoc和Goetz Java Concurrency in Practice的7.1.7节中找到)

我现有的ExecutorService会覆盖newTaskFor。它测试参数的类型并创建FutureTask的子类。这自然支持提交以及invokeAnyinvokeAll

如何为ListenableFuture返回的ListeningExecutorService获得相同的效果?

换句话说,我可以把这段代码放在哪里

if (callable instanceof SomeClass) {
   return new FutureTask<T>(callable) {
        public boolean cancel(boolean mayInterruptIfRunning) {
            System.out.println("Canceling Task");
            return super.cancel(mayInterruptIfRunning);
        }
    };
} else {
    return new FutureTask<T>(callable);
}

这样我的客户端就可以用

执行println语句
ListeningExecutorService executor = ...;
Collection<Callable> callables = ImmutableSet.of(new SomeClass());
List<Future<?>> futures = executor.invokeAll(callables);
for (Future<?> future : futures) {
    future.cancel(true);
}

失败的解决方案

以下列出了我已经尝试过的事情以及它们无法正常工作的原因。

解决方案A

MyExecutorService传递给MoreExecutors.listeningDecorator

问题1:很遗憾,由此产生的ListeningExecutorService(一个AbstractListeningExecutorService)未委托给ExecutorService方法,它委托给execute(Runnable) } Executor上的方法。因此,永远不会调用newTaskFor上的MyExecutorService方法。

问题2: AbstractListeningExecutorService通过静态工厂方法创建Runnable(a ListenableFutureTask),我无法扩展。

解决方案B

newTaskFor内,正常创建MyRunnableFuture,然后使用ListenableFutureTask打包。

问题1: ListenableFutureTask的工厂方法不接受RunnableFuture,他们接受RunnableCallable。如果我将MyRunnableFuture作为Runnable传递,则生成的ListenableFutureTask只会调用run(),而不会调用任何Future方法(我的行为所在的位置)。

问题2:即使它确实调用了我的Future方法,MyRunnableFuture也不是Callable,所以我必须提供返回值创建我没有的ListenableFutureTask ...因此Callable

解决方案C

让MyRunnableFuture扩展ListenableFutureTask而不是FutureTask

问题: ListenableFutureTask现在是最终版(截至r10 / r11)。

解决方案D

MyRunnableFuture延长ForwardingListenableFuture并实施RunnableFuture。然后将SomeClass参数包装在ListenableFutureTask中并从delegate()

返回

问题:它挂了。我不能很好地理解这个问题,但是这个配置会导致FutureTask.Sync出现死锁。

源代码:根据要求,这是解决方案D挂起的来源:

import java.util.*;
import java.util.concurrent.*;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.*;

/** See http://stackoverflow.com/q/8931215/290943 */
public final class MyListeningExecutorServiceD extends ThreadPoolExecutor implements ListeningExecutorService {

    // ===== Test Harness =====

    private static interface SomeInterface {
        public String getName();
    }

    private static class SomeClass implements SomeInterface, Callable<Void>, Runnable {
        private final String name;

        private SomeClass(String name) {
            this.name = name;
        }

        public Void call() throws Exception {
            System.out.println("SomeClass.call");
            return null;
        }

        public void run() {
            System.out.println("SomeClass.run");
        }

        public String getName() {
            return name;
        }
    }

    private static class MyListener implements FutureCallback<Void> {
        public void onSuccess(Void result) {
            System.out.println("MyListener.onSuccess");
        }

        public void onFailure(Throwable t) {
            System.out.println("MyListener.onFailure");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.println("Main.start");

        SomeClass someClass = new SomeClass("Main.someClass");

        ListeningExecutorService executor = new MyListeningExecutorServiceD();
        Collection<Callable<Void>> callables = ImmutableSet.<Callable<Void>>of(someClass);
        List<Future<Void>> futures = executor.invokeAll(callables);

        for (Future<Void> future : futures) {
            Futures.addCallback((ListenableFuture<Void>) future, new MyListener());
            future.cancel(true);
        }

        System.out.println("Main.done");
    }

    // ===== Implementation =====

    private static class MyRunnableFutureD<T> extends ForwardingListenableFuture<T> implements RunnableFuture<T> {

        private final ListenableFuture<T> delegate;
        private final SomeInterface someClass;

        private MyRunnableFutureD(SomeInterface someClass, Runnable runnable, T value) {
            assert someClass == runnable;
            this.delegate = ListenableFutureTask.create(runnable, value);
            this.someClass = someClass;
        }

        private MyRunnableFutureD(SomeClass someClass, Callable<T> callable) {
            assert someClass == callable;
            this.delegate = ListenableFutureTask.create(callable);
            this.someClass = someClass;
        }

        @Override
        protected ListenableFuture<T> delegate() {
            return delegate;
        }

        public void run() {
            System.out.println("MyRunnableFuture.run");
            try {
                delegate.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }

        @Override
        public boolean cancel(boolean mayInterruptIfRunning) {
            System.out.println("MyRunnableFuture.cancel " + someClass.getName());
            return super.cancel(mayInterruptIfRunning);
        }
    }

    public MyListeningExecutorServiceD() {
        // Same as Executors.newSingleThreadExecutor for now
        super(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
    }

    @Override
    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        if (runnable instanceof SomeClass) {
            return new MyRunnableFutureD<T>((SomeClass) runnable, runnable, value);
        } else {
            return new FutureTask<T>(runnable, value);
        }
    }

    @Override
    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        if (callable instanceof SomeClass) {
            return new MyRunnableFutureD<T>((SomeClass) callable, callable);
        } else {
            return new FutureTask<T>(callable);
        }
    }

    /** Must override to supply co-variant return type */
    @Override
    public ListenableFuture<?> submit(Runnable task) {
        return (ListenableFuture<?>) super.submit(task);
    }

    /** Must override to supply co-variant return type */
    @Override
    public <T> ListenableFuture<T> submit(Runnable task, T result) {
        return (ListenableFuture<T>) super.submit(task, result);
    }

    /** Must override to supply co-variant return type */
    @Override
    public <T> ListenableFuture<T> submit(Callable<T> task) {
        return (ListenableFuture<T>) super.submit(task);
    }
}

5 个答案:

答案 0 :(得分:3)

根据这个问题以及我最近的其他几个讨论,我得出的结论是RunnableFuture / FutureTask具有内在的误导性:显然你提交Runnable ,显然你得到了Future,显然基础Thread需要Runnable。但是为什么课程要同时实施RunnableFuture?如果确实如此,Runnable它正在取代?这已经够糟糕了,但随后我们引入了多个级别的执行程序,事情真的失控了。

如果这里有解决方案,我认为需要将FutureTask视为AbstractExecutorService的实施细节。我会专注于将问题分成两部分:

  • 我想有条件地修改返回的Future
  • 我想有条件地修改执行程序服务运行的代码。 (我实际上不确定这是否是一个要求,但是如果有的话,我将覆盖它。即使不是,它可能有助于建立Runnable / Future区别。)

(抱怨Markdown叽叽喳喳)

class MyWrapperExecutor extends ForwardingListeningExecutorService {
  private final ExecutorService delegateExecutor;

  @Override public <T> ListenableFuture<T> submit(Callable<T> task) {
    if (callable instanceof SomeClass) {
      // Modify and submit Callable (or just submit the original Callable):
      ListenableFuture<T> delegateFuture =
          delegateExecutor.submit(new MyCallable(callable));
      // Modify Future:
      return new MyWrapperFuture<T>(delegateFuture);
    } else {
      return delegateExecutor.submit(callable);
    }
  }

  // etc.
}

这可行吗?

答案 1 :(得分:1)

根据ListeningExecutorService Javadoc,您可以使用MoreExecutors.listeningDecorator来装饰您自己的ExecutorService。

因此,使用覆盖newTaskFor的ExecutorService,并使用上面的方法将其包装起来。这对你有用吗?

<强>更新

好的,这就是我要做的事情:

1)如果你还没有下载番石榴来源。

2)不要使用listeningDecorator,而是让自定义ExecutorService实现ListeningExecutorService。

3)FutureTask的子类应该实现ListenableFuture,并从ListenableFutureTask复制代码,这非常简单,然后添加你的取消方法覆盖。

4)通过将现有方法的返回方法更改为ListenableFuture,在自定义ExecutorService上实现ListeningExecutorService的方法。

答案 2 :(得分:1)

我怀疑MoreExecutors.listeningDecorator(service)会修改基础service返回的期货。因此,如果您已经修改了基础ExecutorService,而您只是在修饰Future方法,那么您可以直接使用MoreExecutors.listeningDecorator(service)。你有没有尝试过看看它是否有效?如果没有,你能否提供更多有关为什么不起作用的细节?

---- ---- UPDATE

看起来你的代码,如写的,混合了submit和invokeAll。 (具体来说,它调用invokeAll,它不委托提交...)

那就是说,我正在迅速得出的结论是,ListenableFutures和ListeningExecutorService并不意味着以这种方式被滥用。实现这些没有死锁的东西是一个真正难以解决的问题,如果你能避免它就不应该这样做。

我怀疑提交解决方案A不起作用的番石榴问题可能是值得的,也许。但我一直试图弄清楚如何做你正在做的事情,它只是这样做感觉不对。

你能否就为什么试图回归不同的未来提供一些见解?

答案 3 :(得分:1)

MoreExecutors.listeningDecorator的javadoc明确指出它委托给execute,并且从不致电代理人submitinvokeAllinvokeAny。此外,它表示任何“任务的特殊处理必须在委托的execute方法中实现,或者通过包装返回的ListeningExecutorService来实现。”

所以使用newTaskFor已经结束了。周期。

我可以通过以下方式获得我想要的大部分行为(来源包含在下面):

  1. 将我要添加的行为移至ForwardingListenableFuture的子类(MyListenableFuture)
  2. MyExecutorService延长ForwardingListeningExecutorService
  3. 使用MoreExecutors.listeningDecorator打包标准ExecutorService ,不用重写的newTaskFor方法。
  4. 覆盖提交以包含listenDecorator使用MyListenableFuture返回的ListenableFuture
  5. 我留下的唯一“差距”是invokeAll。有两个问题:

    1. (次要)如果我认为Future返回的listeningDecorator与我提交给它的Callable的“顺序相同”,那么我可以走下去CallableFuture的列表,为每对创建MyListenableFuture。这可能是一个安全的假设,但仍然不完全有效。 (我还必须从我的论点中复制Callable以避免交错变异,但没关系)
    2. (更大)AbstractListeningExecutorServicecancel内调用isDonegetinvokeAll。这意味着我无法在调用这些方法之前将其行为添加到这些方法中。
    3. 来源

      import java.util.concurrent.*;
      import com.google.common.util.concurrent.*;
      
      /** See http://stackoverflow.com/q/8931215/290943 */
      public final class MyListeningExecutorServiceE extends ForwardingListeningExecutorService {
      
          // ===== Test Harness =====
      
          private static interface SomeInterface {
              public String getName();
          }
      
          private static class SomeClass implements SomeInterface, Callable<Void>, Runnable {
              private final String name;
      
              private SomeClass(String name) {
                  this.name = name;
              }
      
              public Void call() throws Exception {
                  System.out.println("SomeClass.call");
                  return null;
              }
      
              public void run() {
                  System.out.println("SomeClass.run");
              }
      
              public String getName() {
                  return name;
              }
          }
      
          private static class MyListener implements FutureCallback<Void> {
              public void onSuccess(Void result) {
                  System.out.println("MyListener.onSuccess");
              }
      
              public void onFailure(Throwable t) {
                  System.out.println("MyListener.onFailure");
              }
          }
      
          public static void main(String[] args) throws InterruptedException {
              System.out.println("Main.start");
      
              SomeInterface someClass = new SomeClass("Main.someClass");
      
              ListeningExecutorService executor = new MyListeningExecutorServiceE();
      
              ListenableFuture<Void> future = executor.submit((Callable<Void>) someClass);
              Futures.addCallback(future, new MyListener());
              future.cancel(true);
      
              /*  Not supported by this implementation
      
              Collection<Callable<Void>> callables = ImmutableSet.<Callable<Void>>of(someClass);
              List<Future<Void>> futures = executor.invokeAll(callables);
      
              for (Future<Void> future : futures) {
                  Futures.addCallback((ListenableFuture<Void>) future, new MyListener());
                  future.cancel(true);
              }
              */
      
              executor.shutdown();
              System.out.println("Main.done");
          }
      
          // ===== Implementation =====
      
          private static class MyListenableFutureE<T> extends ForwardingListenableFuture<T> {
      
              private final ListenableFuture<T> delegate;
              private final SomeInterface someInterface;
      
              private MyListenableFutureE(SomeInterface someInterface, ListenableFuture<T> delegate) {
                  this.delegate = delegate;
                  this.someInterface = someInterface;
              }
      
              @Override
              protected ListenableFuture<T> delegate() {
                  return delegate;
              }
      
              @Override
              public boolean cancel(boolean mayInterruptIfRunning) {
                  System.out.println("MyRunnableFuture.cancel " + someInterface.getName());
                  return super.cancel(mayInterruptIfRunning);
              }
          }
      
          private final ListeningExecutorService delegate;
      
          public MyListeningExecutorServiceE() {
              delegate = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
          }
      
          @Override
          protected ListeningExecutorService delegate() {
              return delegate;
          }
      
          @Override
          public <T> ListenableFuture<T> submit(Callable<T> task) {
              ListenableFuture<T> future = super.submit(task);
      
              if (task instanceof SomeInterface) {
                  future = new MyListenableFutureE<T>((SomeInterface) task, future);
              }
      
              return future;
          }
      
          @Override
          public ListenableFuture<?> submit(Runnable task) {
              ListenableFuture<?> future = super.submit(task);
      
              if (task instanceof SomeInterface) {
                  future = new MyListenableFutureE((SomeInterface) task, future);
              }
      
              return future;
          }
      
          @Override
          public <T> ListenableFuture<T> submit(Runnable task, T result) {
              ListenableFuture<T> future = super.submit(task, result);
      
              if (task instanceof SomeInterface) {
                  future = new MyListenableFutureE<T>((SomeInterface) task, future);
              }
      
              return future;
          }
      }
      

答案 4 :(得分:0)

对我先前提案的invokeAll问题有很好的了解。

也许我仍然在思考这个问题。 MyRunnableFuture本身可以ListenableFutureMyExecutorService实施ListeningExecutorService吗?您仍会延长AbstractExecutorService,但您也声明您正在实施ListeningExecutorService。因为AbstractExecutorService的所有方法都使用newTaskFor返回的对象,并且因为您知道您将从该方法返回ListenableFuture,所以您可以简单地覆盖它们以声明返回ListenableFuture类型并按return (ListenableFuture<T>) super.foo()

的方式实现它们

如果这样可行,或许我们应该将我们的AbstractListeningExecutorService类公开并添加一些可以使这更容易的钩子?