最终目标是根据Callable
/ Runnable
参数的类型向ListenableFuture s 添加额外行为。我想为每个Future方法添加额外的行为。 (示例用例可以在AbstractExecutorService's javadoc和Goetz Java Concurrency in Practice的7.1.7节中找到)
我现有的ExecutorService会覆盖newTaskFor。它测试参数的类型并创建FutureTask
的子类。这自然支持提交以及invokeAny和invokeAll。
如何为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);
}
以下列出了我已经尝试过的事情以及它们无法正常工作的原因。
将MyExecutorService
传递给MoreExecutors.listeningDecorator。
问题1:很遗憾,由此产生的ListeningExecutorService(一个AbstractListeningExecutorService
)未委托给ExecutorService方法,它委托给execute(Runnable) } Executor上的方法。因此,永远不会调用newTaskFor
上的MyExecutorService
方法。
问题2: AbstractListeningExecutorService
通过静态工厂方法创建Runnable(a ListenableFutureTask),我无法扩展。
在newTaskFor
内,正常创建MyRunnableFuture
,然后使用ListenableFutureTask
打包。
问题1: ListenableFutureTask的工厂方法不接受RunnableFuture,他们接受Runnable
和Callable
。如果我将MyRunnableFuture
作为Runnable传递,则生成的ListenableFutureTask
只会调用run()
,而不会调用任何Future
方法(我的行为所在的位置)。
问题2:即使它确实调用了我的Future
方法,MyRunnableFuture
也不是Callable
,所以我必须提供返回值创建我没有的ListenableFutureTask
...因此Callable
。
让MyRunnableFuture扩展ListenableFutureTask
而不是FutureTask
问题: ListenableFutureTask
现在是最终版(截至r10 / r11)。
让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);
}
}
答案 0 :(得分:3)
根据这个问题以及我最近的其他几个讨论,我得出的结论是RunnableFuture
/ FutureTask
具有内在的误导性:显然你提交Runnable
,显然你得到了Future
,显然基础Thread
需要Runnable
。但是为什么课程要同时实施Runnable
和Future
?如果确实如此,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
,并且从不致电代理人submit
,invokeAll
和invokeAny
。此外,它表示任何“任务的特殊处理必须在委托的execute
方法中实现,或者通过包装返回的ListeningExecutorService
来实现。”
所以使用newTaskFor
已经结束了。周期。
我可以通过以下方式获得我想要的大部分行为(来源包含在下面):
ForwardingListenableFuture
的子类(MyListenableFuture)MyExecutorService
延长ForwardingListeningExecutorService
MoreExecutors.listeningDecorator
打包标准ExecutorService
,不用重写的newTaskFor
方法。我留下的唯一“差距”是invokeAll
。有两个问题:
Future
返回的listeningDecorator
与我提交给它的Callable
的“顺序相同”,那么我可以走下去Callable
和Future
的列表,为每对创建MyListenableFuture
。这可能是一个安全的假设,但仍然不完全有效。 (我还必须从我的论点中复制Callable
以避免交错变异,但没关系)AbstractListeningExecutorService
在cancel
内调用isDone
,get
和invokeAll
。这意味着我无法在调用这些方法之前将其行为添加到这些方法中。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
本身可以ListenableFuture
和MyExecutorService
实施ListeningExecutorService
吗?您仍会延长AbstractExecutorService
,但您也声明您正在实施ListeningExecutorService
。因为AbstractExecutorService
的所有方法都使用newTaskFor
返回的对象,并且因为您知道您将从该方法返回ListenableFuture
,所以您可以简单地覆盖它们以声明返回ListenableFuture
类型并按return (ListenableFuture<T>) super.foo()
。
如果这样可行,或许我们应该将我们的AbstractListeningExecutorService
类公开并添加一些可以使这更容易的钩子?