Добавить / расширить поведение будущего, созданного ListeningExecutorService
конечной целью является добавление дополнительного поведения в ListenableFutures на основе типа Callable
/Runnable
аргумент. Я хочу добавить дополнительное поведение к каждому из будущее методы. (Примеры использования можно найти в javadoc AbstractExecutorService и раздел 7.1.7 Гетца параллелизм Java на практике)
у меня есть ExecutorService, который переопределяет newTaskFor. Он проверяет тип аргумента и создает подкласс FutureTask
. Это, естественно, поддерживает submit, а также invokeAny и invokeAll.
как получить тот же эффект для ListenableFuture s возвращается 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
to MoreExecutors.listeningDecorator.
Проблема 1: к сожалению, в результате ListeningExecutorService (an AbstractListeningExecutorService
) не делегировать ExecutorService методы, делегаты execute (Runnable) метод on исполнитель. В результате newTaskFor
метод on MyExecutorService
никогда не вызывается.
Проблема 2: AbstractListeningExecutorService
создает Runnable (a ListenableFutureTask) через статический заводской метод, который я не могу расширить.
Решение B
внутри newTaskFor
, create MyRunnableFuture
нормально, а затем оберните его с ListenableFutureTask
.
Проблема 1: ListenableFutureTaskзаводские методы не принимают результат того,s, они принимают Runnable
и Callable
. Если я пройду MyRunnableFuture
как Runnable, в результате ListenableFutureTask
просто называет run()
и Future
методы (где мое поведение).
Проблема 2: даже если он назвал мой Future
методы MyRunnableFuture
- это не Callable
, поэтому я должен предоставить возвращаемое значение при создании ListenableFutureTask
... которого у меня нет... следовательно the Callable
.
Решение C
пусть MyRunnableFuture продлить ListenableFutureTask
вместо FutureTask
: ListenableFutureTask
теперь окончательно (по состоянию на r10 / r11).
Решение D
пусть MyRunnableFuture
расширения ForwardingListenableFuture и реализовать результат того,. Затем оберните в ListenableFutureTask
и вернуть, что из delegate()
: он зависает. Я не понимаю проблему достаточно хорошо, чтобы объяснить ее, но эта конфигурация вызывает тупик в FutureTask.Синхронизация.
Исходный Код: по запросу, вот источник решения 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 ответов
основываясь на этом вопросе и нескольких других обсуждениях, которые у меня были недавно, я прихожу к выводу, что RunnableFuture
/FutureTask
по своей сути вводит в заблуждение: ясно, что вы представляете Runnable
, и ясно, что вы получить Future
назад, и явно лежащий в основе Thread
нужен Runnable
. Но почему класс должен реализовывать оба Runnable
и Future
? И если это произойдет, то что?--3--> - это его замена? Это уже достаточно плохо, но затем мы вводим несколько уровней исполнителей, и все действительно становится из руки.
если здесь есть решение, я думаю, что это требует лечения FutureTask
как деталь реализации AbstractExecutorService
. Вместо этого я бы сосредоточился на разделении проблемы на две части:
- я хочу условно изменить возвращаемый
Future
. - я хочу условно изменить код, выполняемый службой исполнителя. (На самом деле я не уверен, является ли это требование, но я прикрою ее в случае. Даже если нет, это может помогите установить
Runnable
/Future
различие.)
(ворчание уценки ворчание)
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.
}
может ли это сработать?
согласно ListeningExecutorService Javadoc, Вы можете использовать MoreExecutors.listeningDecorator, чтобы украсить свой собственный ExecutorService.
поэтому используйте ExecutorService, который переопределяет newTaskFor, и оберните его указанным выше методом. Это сработает для тебя?
обновление
ОК, Вот что я бы сделал:
1) Загрузите источники Guava, если вы еще этого не сделали.
2) Не используйте listeningDecorator, вместо этого сделайте свой пользовательский ExecutorService реализовать ListeningExecutorService.
3) Ваш подкласс FutureTask должен реализовать ListenableFuture и скопировать код из ListenableFutureTask, что довольно просто, а затем добавить переопределение метода cancel.
4) реализуйте методы ListeningExecutorService в пользовательском ExecutorService, изменив метод возврата существующих методов на ListenableFuture.
Я подозреваю, что MoreExecutors.listeningDecorator(service)
изменит фьючерсы, возвращенные вашим базовым service
. Поэтому, если вы уже изменили свой базовый ExecutorService
, и вы просто украшаете Future
методы, вы можете просто использовать MoreExecutors.listeningDecorator(service)
напрямую. Вы экспериментировали, чтобы убедиться, что это работает? Если нет, можете ли вы предоставить более подробную информацию о том, почему это не сработает?
----обновление----
похоже, что ваш код, как написано, смешивает submit и invokeAll. (В частности, вызывает invokeAll,который не делегирует для отправки...)
что сказал, выводу, я быстро рисунок ListenableFutures и ListeningExecutorService не должны злоупотреблять этим способом. Реализация этого материала без тупиков-настоящая трудная проблема, и вы не должны этого делать, если можете этого избежать.
Я подозреваю, что, возможно, стоит подать вопрос гуавы, что решение A не работает,может быть. но я продолжаю пытаться выяснить, как делать то, что ты делаешь, и это кажется неправильным.
можете ли вы дать некоторое представление как почему вы пытаетесь вернуть разные фьючерсы?
javadoc для MoreExecutors.listeningDecorator ясно заявляет, что он делегирует execute
и никогда не вызывает делегата submit
, invokeAll
и invokeAny
. Кроме того, в нем говорится, что любая "специальная обработка задач должна быть реализована в делегате execute
метод или путем обертывания возвращенного ListeningExecutorService
."
используя newTaskFor
выходит. Период.
я могу получить большую часть поведения, которое я хочу, делая (источник включен ниже):
- переместить поведение, которое я хочу добавить в подкласс
ForwardingListenableFuture
(MyListenableFuture) - пусть
MyExecutorService
расширенияForwardingListeningExecutorService
- использовать
MoreExecutors.listeningDecorator
обернуть стандартExecutorService
без переопределенныйnewTaskFor
метод. - переопределить submit для обертывания ListenableFuture, возвращаемого listeningDecorator с помощью MyListenableFuture
единственный "пробел", который у меня остался, это invokeAll
. Есть два проблемы:
- (минор) если я предполагаю
Future
s возвращаетсяlisteningDecorator
"в том же порядке" какCallable
s, что я подал на него, то я могу пройти по спискуCallable
s иFuture
s созданиеMyListenableFuture
для каждой пары. Это, вероятно, безопасное предположение, но все же не совсем верное. (Мне также придется скопироватьCallable
S из моего аргумента, чтобы избежать перемежающейся мутации, но это нормально) - (больше)
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;
}
}
хороший улов на invokeAll
проблемы с моим предыдущим предложением.
может быть, я все еще слишком много думаю об этом. Может реализовать ListenableFuture
и MyExecutorService
реализует ListeningExecutorService
? Вы все равно продлите AbstractExecutorService
, но вы объявите, что вы реализуете ListeningExecutorService
, так же. Потому что все методы AbstractExecutorService
используйте объект, возвращаемый newTaskFor
, и потому что вы знаете, что вы вернетесь ListenableFuture
из этого метода, вы можете просто переопределить все их объявить возвращаемый тип ListenableFuture
и реализовать их по линиям return (ListenableFuture<T>) super.foo()
.
если это сработает, возможно, мы должны сделать наш AbstractListeningExecutorService
class public и добавить некоторые крючки, которые сделают это проще?