如何立即停止使用ExecutorService启动的任务?

时间:2011-12-26 03:38:48

标签: java concurrency 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 < / strong>我无法知道任务是从call()方法中停止的。

我正在考虑在调用future.cancel(true)并在call()方法中不断检查该标志后设置一个像 canceled = 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()中,我只是启动与远程服务器的套接字连接以获取文件的大小(这肯定需要一些时间),在成功获取fileSize后,我将启动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来尝试你的建议,但没有任何结果。

修改

此问题已在此链接中解决:Can't stop a task which is started using ExecutorService

3 个答案:

答案 0 :(得分:4)

Future.cancel(boolean)的{​​{3}}说:

  

如果任务已经启动,那么mayInterruptIfRunning   参数确定执行此任务的线程是否应该是   因试图停止任务而中断。

所以很可能确定执行任务的线程被中断了。可能发生的是其中一个

  

......在这里做很多其他的事情......

意外清除Thread的{​​{1}}状态而未执行所需操作 处理。如果您在interrupted中设置断点,则可能会遇到犯罪分子。

我能想到的另一个选择是任务在捕获中断之前终止,因为它已完成或抛出一些未捕获的异常。致电Thread.interrupt()确定。无论如何,正如asdasd所提到的,设置Future.get()是一个好习惯。

答案 1 :(得分:2)

你正在做的是非常危险的:你正在使用一个线程池来执行任务(我称之为下载程序),以及相同的线程池来执行任务

  • 等待下载程序完成(我称之为控制器)
  • 或要求控制器停止

这意味着如果在控制器启动后达到核心线程数,则下载程序将被放入线程池的队列中,并且控制器线程将永远不会完成。同样,如果在执行取消任务时达到了核心线程数,则此取消任务将被放入队列中,并且在完成其他任务之前不会执行。

你应该使用一个线程池作为下载程序,另一个用于控制器,以及当前线程来取消控制器。

答案 2 :(得分:0)

我想你会找到解决方案here。重点是cancel方法引发InterruptedException。取消后请检查您的主题是否仍在运行?你确定你没有尝试中断完成的线程吗?你确定你的线程没有失败任何其他异常?尝试设置UncaughtExceptionHandler。