Ява - фьючерсы не отменяются
У меня была проблема с моим фьючерсным контрактом, возвращенным из моего ExecutorService, который не отменялся должным образом в течение некоторого времени. Я написал MCVE, который, я считаю, отражает корень моих проблем.
Вот код:
public class MainTest extends Application {
private ExecutorService threadPool = Executors.newFixedThreadPool(8);
private int lowResolution = 20;
private List<Future<Object>> lowResFutureList =
Collections.synchronizedList(new ArrayList<Future<Object>>());
private List<Future<Object>> highResFutureList =
Collections.synchronizedList(new ArrayList<Future<Object>>());
private Slider slider = new Slider();
@Override
public void start(Stage primaryStage) {
slider.valueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
updateLowResImage();
}
});
slider.showTickLabelsProperty().set(true);
slider.showTickMarksProperty().set(true);
slider.setMax(60);
Scene scene = new Scene(slider);
primaryStage.setTitle("Test");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public void updateLowResImage() {
System.out.println("HighResFutures: " + highResFutureList.size() + "\n"
+ "LowResFutures: " + lowResFutureList.size());
for(Future<Object> future : lowResFutureList) {
if(future != null) {
System.out.println("Attempting low res cancel: " + future.cancel(false));
}
}
new Thread(new Runnable() {
@Override
public void run() {
Future<Object> future = threadPool.submit(new ImageTask(lowResolution, lowResolution));
lowResFutureList.add(future);
try {
future.get();
System.out.println("Low res Future completed.");
} catch (Exception e) {
System.out.println("Low resolution Future cancelled.");
} finally {
lowResFutureList.remove(future);
}
}
}).start();
int resolution = (int) slider.getValue();
if(resolution > lowResolution) {
updateHighResImage(resolution);
}
}
public synchronized void updateHighResImage(int resolution) {
for(Future<Object> future : highResFutureList) {
if(future != null) {
System.out.println("Attempting high res cancel: " + future.cancel(true));
}
}
new Thread(new Runnable() {
@Override
public void run() {
Future<Object> future = threadPool.submit(new ImageTask(resolution, resolution));
highResFutureList.add(future);
try {
future.get();
System.out.println("High res Future completed.");
} catch (Exception e) {
System.out.println("High resolution Future cancelled.");
} finally {
highResFutureList.remove(future);
}
}
}).start();
}
private class ImageTask implements Callable<Object> {
int width = 0;
int height = 0;
private ImageTask(int width, int height) {
this.width = width;
this.height = height;
}
@Override
public Object call() throws Exception {
for(int x = 0; x < width; x++) {
for(int y = 0; y < height; y++) {
if(Thread.interrupted()) {
System.out.println("Interrupted!");
return null;
}
try {
Thread.sleep(1);
} catch(Exception e) {
System.out.print("Cancelled! ");
}
}
}
System.out.println("Continued!");
return null;
}
}
}
Я считаю, что некоторые объяснения необходимы. Таким образом, код поднимает Slider
с диапазоном от 0 до 60. Каждый раз, когда ползунок меняет свое значение, он должен создавать Task
это составляет 20^2 миллисекунд. Когда это сделано, он запускает другой поток, который считает slider.getValue()
^ 2 миллисекунды, если slider.getValue()
больше 20. Эти Task
s представлены на ExecutorService
,
Теперь, если Slider
меняет свое значение в то время как разные Task
они уже считают миллисекунды эти Task
s должны быть отменены, а новые должны начать отсчет.
Как видите, в коде есть несколько операторов печати. Если я перееду Slider
энергично близко к 50, я ожидаю увидеть что-то вроде цитаты ниже, когда я отпущу Slider
,
HighResFutures: 1
LowResFutures: 1
Попытка отмены низкого разрешения: ложь
Попытка отмены высокого разрешения: true
Прерванный!
Низкое разрешение Будущее отменено.
Высокое разрешение Будущее отменено.
Продолжение!
Низкое разрешение Будущее завершено.
Продолжение!
Высокое разрешение Будущее завершено.
Но то, что я вижу, это:
HighResFutures: 1
LowResFutures: 1
Попытка отмены низкого разрешения: правда
Попытка отмены высокого разрешения: true
Низкое разрешение Будущее отменено.
Высокое разрешение Будущее отменено.
Продолжение!
Продолжение!
Продолжение!
Продолжение!
Низкое разрешение Будущее завершено.
Продолжение!
Продолжение!
Продолжение!
Продолжение!
Продолжение!
Продолжение!
Высокое разрешение Будущее завершено.
Итак, первое, что нужно отметить, это то, что отмена низкого разрешения Future
S успешно, хотя он не должен быть в состоянии отменить текущий Task
s (это значит, что у него не было времени начать?). Во-вторых, я никогда не получаю "Прервано!" распечатаны. И, наконец, "Продолжение!" печатается несколько раз до завершения финала Task
что заставляет меня верить, что почти никто из Task
s отменены должным образом. Что вы думаете об этом? Я очень новичок в ExecutorService
и понятия не имею, что происходит. Буду признателен за любую оказанную помощь.