如何立即停止使用ExecutorService启动的任务?
我已经尝试了许多不同的方法来立即停止使用ExecutorService开始的任务,但没有运气。如何立即停止使用ExecutorService启动的任务?
Future<Void> future = executorService.submit(new Callable<Void>(
public Void call() {
... do many other things here..
if(Thread.currentThread.isInterrupted()) {
return null;
}
... do many other things here..
if(Thread.currentThread.isInterrupted()) {
return null;
}
}
));
if(flag) { // may be true and directly cancel the task
future.cancel(true);
}
有时候,我需要取消任务,它刚开始后,你可能会好奇,为什么我要做到这一点,那么你可以想像一个塞纳里奥,用户不小心点击“下载”按钮开始“下载任务”,他立即想取消该操作,因为这只是一次意外点击。
的问题是,调用future.cancel后(真),任务没有停止,Thread.currentThread.isInterrupted()仍然返回假,我也没有办法知道任务停止从call()方法中。
我想在设置一个像取消= true之后调用future.cancel(true)并在call()方法中不断检查该标志,我认为这是一个破解,代码可能非常难看因为用户可以在同一时刻开始很多任务。
有没有一种更优雅的方式来实现我想要的?
编辑:
这真的把我气得火冒三丈。我已经在这个问题上花了差不多一天的时间了。我将尝试为我面临的问题多解释一点。
我按照以下步骤启动5个任务,每个任务将启动5个线程下载一个文件。然后我立即停止所有5个任务。对于下面的所有方法调用,我开始一个线程(ExecutorService.submit(task)),使它成为异步的,就像你可以从方法的后缀中看到的那样。
int t1 = startTaskAysnc(task1);
int t2 = startTaskAysnc(task2);
int t3 = startTaskAysnc(task3);
int t4 = startTaskAysnc(task4);
int t5 = startTaskAysnc(task5);
int stopTaskAysnc(t1);
int stopTaskAysnc(t2);
int stopTaskAysnc(t3);
int stopTaskAysnc(t4);
int stopTaskAysnc(t5);
在startTaskAysnc(),我只是启动套接字连接到远程服务器,以获取该文件的大小(这当然是要花一些时间),成功后获得的档案大小,我将开始5个线程下载文件的不同部分。像下面的(代码简化,使其更易于遵循):
public void startTaskAsync(DownloadTask task) {
Future<Void> future = executorService.submit(new Callable<Void>(
public Void call() {
// this is a synchronous call
int fileSize = getFileSize();
System.out.println(Thread.currentThread.isInterrupted());
....
Future<Void> futures = new Future<Void>[5];
for (int i = 0; i < futures.length; ++i) {
futures[i] = executorService.submit(new Callable<Void>(){...});
}
for (int i = 0; i < futures.length; ++i) {
futures[i].get(); // wait for it to complete
}
}
));
synchronized (mTaskMap) {
mTaskMap.put(task.getId(), future);
}
}
public void stopTaskAysnc(int taskId) {
executorService.execute(new Runnable(){
Future<Void> future = mTaskMap.get(taskId);
future.cancel(true);
});
}
我注意到一个奇怪的行为,我叫stopTaskAsync()后,所有5个任务,总会有至少一个任务得到停止(即Thread.currentThread.isInterrupted()返回true),其他4个任务保持运行。
我试着通过设置一个UncaughtExceptionHandler来尝试你的建议,但没有任何结果。
编辑:
的问题是在这个环节解决:Can't stop a task which is started using ExecutorService
我想你会发现解决方案here。重点是取消方法引发InterruptedException。请检查您的线程在取消后是否仍在运行?你确定你没有试图中断完成的线程吗?你确定你的线程没有与其他异常失败吗?尝试设置UncaughtExceptionHandler。
嗯,Future.cancel(boolean)
的javadoc说:
如果任务已经启动,则mayInterruptIfRunning 参数决定是否执行此任务的线程应该 在试图停止任务中断。
因此很确定执行任务的线程是中断。什么可能发生的是,的
一个......做很多其他的事情在这里..
意外清除Thread
的interrupted
状态,而不执行所需的 处理。如果你在Thread.interrupt()
中设置断点,你可能会抓到罪犯。
我能想到的另一个选择是任务在捕获中断之前终止,或者是因为它已完成或抛出一些未捕获的异常。致电Future.get()
确定。无论如何,正如asdasd提到的那样,设置UncaughtExceptionHandler
是一个好习惯。
嗨,@yair,我编辑了我的问题,如果你有线索,请给我一些帮助。谢谢。 – neevek 2011-12-26 08:34:02
你正在做什么是非常危险的:你使用一个线程池来执行任务(我会打电话给下载者),和相同的线程池来执行任务,其
- 等待下载者来完成(我称之为控制器)
- 或要求控制器停止
这意味着,如果控制器开始后到达线程的核心数,下载者将被放置在线程池队列和控制器线程永远不会完成。同样,如果在执行取消任务时达到线程的核心数量,则取消任务将放入队列中,直到其他任务完成时才会执行。
您应该为下载程序使用线程池,为控制器使用另一个线程池,并使用当前线程取消控制器。
嗨,JB Nizet。你是对的,在发布这个问题之前,我已经意识到了这一点,但是我面对的问题似乎与使用线程池的策略没有任何关系,因为我确保了最少的线程数足以运行所有的线程控制器和下载5个任务。无论如何,我会采取你的建议,并使用2个独立的线程池来运行控制器和下载程序,并取消当前线程中的控制器。 – neevek 2011-12-26 10:38:32
嗨,@asdasd,我编辑了我的问题,如果您有线索,请给我一些帮助。谢谢。 – neevek 2011-12-26 08:34:19