Расширение FutureTask, как обработать отмену

Я использовал NetDataSerialiser класс для сериализации моих доменных классов. Класс .

NetDataContractSerializer доменные классы совместно используются клиентом и сервером.

5
задан Thomas Jung 27 November 2009 в 17:11
поделиться

2 ответа

Вы правы, что там гонка. FutureTask # done () будет вызываться не более одного раза , поэтому, если задача уже была отменена до того, как она запускалась через RunnableFuture # run () , вы пропустили вызов FutureTask # done () .

Рассматривали ли вы более простой подход, который всегда выдает симметричный набор парных вызовов ITaskStatusHandler # taskRunning () и ITaskStatusHandler # taskCompleted () , например?

@Override
public void run() {
  statusHandler.TaskRunning(this);
  try {
    super.run();
  finally {
    statusHandler.TaskCompleted(this);
  }
}

После вызова RunnableFuture # run () ваша задача действительно выполняется или, по крайней мере, пытается быть запущена. После выполнения FutureTask # run () ваша задача больше не будет выполняться. Так получилось, что в случае отмены переход происходит (почти) немедленно.

Попытка избежать вызова ITaskStatusHandler # taskRunning () , если внутренний Callable или Runnable никогда не вызывается FutureTask # run () потребует от вас установить некоторую общую структуру между Callable или Runnable и самим производным типом FutureTask , чтобы при первом вызове вашей внутренней функции вы установить какой-либо флаг, который внешний FutureTask -производный тип может воспринимать как защелку, указывая, что да, функция действительно начала выполняться до того, как была отменена. Однако к тому времени вы должны были вызвать ITaskStatusHandler # taskRunning () , так что это различие не так полезно.

В последнее время я боролся с аналогичной проблемой проектирования,

4
ответ дан 14 December 2019 в 13:38
поделиться

Ваша проблема в том, что ваши будущие задачи все еще выполняются после того, как вы вызвали для них отмену, верно?

После того, как задача была отправлена ​​в службу исполнителя, она должна управляться исполнителем. (Вы все равно можете отменить отдельную задачу, если хотите.) Вы должны отменить выполнение с помощью метода исполнителя shutdownNow . (Это вызовет метод отмены всех отправленных задач.) Завершение работы по-прежнему будет выполнять все отправленные задачи.

В противном случае исполнитель не «знает», что задача была отменена. Он вызовет метод независимо от внутреннего состояния будущей задачи.

Самый простой подход - использовать структуру Executor как есть и написать декоратор Callable.

class CallableDecorator{

  CallableDecorator(Decorated decorated){
     ...
  }

  setTask(FutureTask task){
    statusHandler.taskCreated(task);
  }

  void call(){
     try{
       statusHandler.taskRunning(task);
       decorated.call();
     }finally{
       statusHandler.taskCompleted(task);
     }
  }
}

Единственная проблема заключается в том, что задача не может находиться в конструктор декоратора. (Это' sa параметра будущего конструктора задачи.) Чтобы разорвать этот цикл, вы должны использовать сеттер или прокси-сервер с внедрением конструктора. Возможно, это вообще не требуется для обратного вызова, и вы можете сказать: statusHandler.callableStarted (Decorated) .

В зависимости от ваших требований вам может потребоваться сигнализация об исключениях и прерываниях.

Базовая реализация:

class CallableDecorator<T> implements Callable<T> {

    private final Callable<T> decorated;
    CallableDecorator(Callable<T> decorated){
        this.decorated = decorated;
    }

    @Override public T call() throws Exception {
        out.println("before " + currentThread());
        try {
            return decorated.call();
        }catch(InterruptedException e){
            out.println("interupted " + currentThread());
            throw e;
        }
        finally {
            out.println("after " + currentThread());
        }
    }
}

ExecutorService executor = newFixedThreadPool(1);
Future<Long> normal = executor.submit(new CallableDecorator<Long>(
        new Callable<Long>() {
            @Override
            public Long call() throws Exception {
                return System.currentTimeMillis();
            }
        }));
out.println(normal.get());

Future<Long> blocking = executor.submit(new CallableDecorator<Long>(
        new Callable<Long>() {
            @Override
            public Long call() throws Exception {
                sleep(MINUTES.toMillis(2)); // blocking call
                return null;
            }
        }));

sleep(SECONDS.toMillis(1));
blocking.cancel(true); // or executor.shutdownNow();

Вывод:

before Thread[pool-1-thread-1,5,main]
after Thread[pool-1-thread-1,5,main]
1259347519104
before Thread[pool-1-thread-1,5,main]
interupted Thread[pool-1-thread-1,5,main]
after Thread[pool-1-thread-1,5,main]
2
ответ дан 14 December 2019 в 13:38
поделиться