我的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()
大于20,则该线程计数slider.getValue()
^ 2毫秒。这些Task
被提交到ExecutorService
。
现在,如果Slider
改变了它的值,而不同的Task
已经开始计数毫秒了,这些Task
应该被取消,而新的应该开始计数。
如您所见,代码中有一些打印语句。如果我将Slider
剧烈移近50,我放开Slider
时会看到类似下面的引用。
HighResFutures:1
LowResFutures:1
尝试低分辨率取消:false
尝试高分辨率时取消:true
中断了!
低分辨率未来已取消。
高分辨率未来已取消。
续!
低分辨率未来完成。
续!
高分辨率未来完成。
但是我看到的是这个
HighResFutures:1
LowResFutures:1
尝试低分辨率取消:true
尝试高分辨率时取消:true
低分辨率未来已取消。
高分辨率未来已取消。
续!
续!
续!
续!
低分辨率未来完成。
续!
续!
续!
续!
续!
续!
高分辨率未来完成。
因此,首先要注意的是,即使不应该取消正在进行的Future
,低分辨率Task
的取消也是成功的(这意味着它没有取消该开始了吗?)。其次,我永远也不会“打扰”!印刷。最后,“继续!”在最后一个Task
完成之前打印了好几次,这使我相信Task
中几乎没有一个被正确取消。你对此有什么看法?我是ExecutorService
的新手,不知道发生了什么。非常感谢您的帮助。