Я использовал NetDataSerialiser
класс для сериализации моих доменных классов. Класс .
NetDataContractSerializer доменные классы совместно используются клиентом и сервером.
Вы правы, что там гонка. 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 ()
, так что это различие не так полезно.
В последнее время я боролся с аналогичной проблемой проектирования,
Ваша проблема в том, что ваши будущие задачи все еще выполняются после того, как вы вызвали для них отмену, верно?
После того, как задача была отправлена в службу исполнителя, она должна управляться исполнителем. (Вы все равно можете отменить отдельную задачу, если хотите.) Вы должны отменить выполнение с помощью метода исполнителя 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]