如何立即停止使用 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(true) 后,任务没有停止,Thread.currentThread.isInterrupted() 仍然返回 false我无法知道任务是从 call() 方法内部停止的。
我正在考虑在调用 future.cancel(true) 并在 call() 方法中不断检查该标志后设置一个类似 cancelled=true 的标志,我认为这是一个 hack,代码可能非常很难看,因为用户可以同时启动许多任务。
有没有更优雅的方式来实现我想要的?
编辑:
这真的让我抓狂。我现在已经在这个问题上花了将近一天的时间了。我将尝试对我面临的问题进行更多解释。
我执行以下操作来启动 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);
});
}
我注意到一种奇怪的行为,在我为所有 5 个任务调用 stopTaskAsync() 后,总会有至少一个任务停止(即Thread.currentThread.isInterrupted()返回true),其他4个任务继续运行。
我已经通过设置 UncaughtExceptionHandler 尝试了您的建议,但没有任何结果。
编辑:
问题已在此链接中解决:无法停止使用 ExecutorService 启动的任务
I have tried many different ways to immediately stop a task which is started using an ExecutorService, with no luck.
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);
}
Sometimes I need to cancel the task immediately after it is started, you may be curious why I want to do this, well you may imagine a senario that a user accidentally hits the "Download" button to start a "Download Task" and he immediately wants to cancel the action because it was just an accidental click.
The problem is that after calling future.cancel(true), the task is not stopped and Thread.currentThread.isInterrupted() still returns false and I have no way to know the task was stopped from inside the call() method.
I am thinking of setting a flag like cancelled=true after calling future.cancel(true) and checking that flag constantly in the call() method, I think this is a hack and the code could be very ugly because the user can start many tasks at the same moment.
Is there a more elegant way of achieving what I want?
EDIT:
This really drives me mad. I have spent almost a day on this problem now. I will try to explain a little bit more for the problem I am facing.
I do the following to start 5 tasks, each task will start 5 threads to download a file. and then I stop all 5 tasks immediately. For all of the method calls below, i start a thread(ExecutorService.submit(task)) to make it asynchronous as you can tell from the suffixes of the methods.
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);
in startTaskAysnc(), I simply initiate a socket connection to remote server to get the size of the file(and this certainly is gonna take some time), after successfully getting the fileSize, I will start 5 threads to download different parts of the file. like the following(the code is simplified to make it more easy to follow):
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);
});
}
I noticed a weird behavior that after I called stopTaskAsync() for all 5 tasks, there would always be at least one task that got stopped(i.e. Thread.currentThread.isInterrupted() return true), and the other 4 tasks kept running.
And I have tried your suggestions by setting an UncaughtExceptionHandler, but nothing comes out from that.
EDIT:
The problem was solved in this link: Can't stop a task which is started using ExecutorService
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
好吧,javadoc<
Future.cancel(boolean)
的 /a> 表示:因此可以肯定的是,执行任务的线程被中断了。可能发生的情况之一是
意外地清除了
Thread
的interrupted
状态而不执行所需的操作处理。如果您在 Thread.interrupt() 中放置断点,您可能会抓住罪犯。
我能想到的另一个选择是任务在捕获中断之前终止,因为它已完成或抛出了一些未捕获的异常。调用
Future.get()
来确定。无论如何,正如 asdasd 提到的,设置 UncaughtExceptionHandler 是一个很好的做法。Well, the javadoc of
Future.cancel(boolean)
says that:so it's quite certain that the thread that executes the task is interrupted. What could have happened is that one of the
is accidentally clearing the
Thread
'sinterrupted
status without performing the desiredhandling. If you'll put a breakpoint in
Thread.interrupt()
you might catch the criminal.Another option I can think of is that the task terminates before capturing the interrupt, either because it's completed or thrown some uncaught exception. Call
Future.get()
to determine that. Anyway, as asdasd mentioned, it is a good practice to set anUncaughtExceptionHandler
.您正在做的事情非常危险:您正在使用线程池来执行任务(我将其称为下载器),并使用相同的线程池来执行
这意味着,如果在控制器启动后达到核心线程数,下载器将被放入线程池的队列中,并且控制器线程将永远不会完成。同样,如果执行取消任务时达到了核心线程数,则该取消任务将被放入队列中,直到其他任务完成后才会执行。
您可能应该为下载程序使用一个线程池,为控制器使用另一个线程池,并使用当前线程来取消控制器。
What you're doing is very dangerous: you're using a thread pool to execute tasks (which I'll call downloaders), and the same thread pool to execute tasks which
This means that if the core number of threads is reached after the controller has started, the downloaders will be put in the queue of the thread pool, and the controller thread will never finish. Similarly, if the core number of threads is reached when you execute the cancelling task, this cancelling task will be put in the queue, and won't execute until some other task is finished.
You should probably use a thread pool for downloaders, another one for controllers, and the current thread to cancel the controllers.
我想您会在这里找到解决方案。要点是 cancel 方法会引发 InterruptedException。请检查取消后您的线程是否仍在运行?您确定没有尝试中断已完成的线程吗?您确定您的线程没有因任何其他异常而失败吗?尝试设置UncaughtExceptionHandler。
I think you'll find solution here. The main point is that cancel method raises InterruptedException. Please check if your thread is still running after cancellation? Are you sure that you didn't try to interrupt finished thread? Are you sure that your thread didn't fail with any other Exception? Try to set up UncaughtExceptionHandler.