2011-12-26 131 views
2

我已经尝试了许多不同的方法来立即停止使用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

回答

0

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

+0

嗨,@asdasd,我编辑了我的问题,如果您有线索,请给我一些帮助。谢谢。 – neevek 2011-12-26 08:34:19

4

嗯,Future.cancel(boolean)javadoc说:

如果任务已经启动,则mayInterruptIfRunning 参数决定是否执行此任务的线程应该 在试图停止任务中断。

因此很确定执行任务的线程是中断。什么可能发生的是,的

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

意外清除Threadinterrupted状态,而不执行所需的 处理。如果你在Thread.interrupt()中设置断点,你可能会抓到罪犯。

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

+0

嗨,@yair,我编辑了我的问题,如果你有线索,请给我一些帮助。谢谢。 – neevek 2011-12-26 08:34:02

2

你正在做什么是非常危险的:你使用一个线程池来执行任务(我会打电话给下载者),和相同的线程池来执行任务,其

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

这意味着,如果控制器开始后到达线程的核心数,下载者将被放置在线程池队列和控制器线程永远不会完成。同样,如果在执行取消任务时达到线程的核心数量,则取消任务将放入队列中,直到其他任务完成时才会执行。

您应该为下载程序使用线程池,为控制器使用另一个线程池,并使用当前线程取消控制器。

+0

嗨,JB Nizet。你是对的,在发布这个问题之前,我已经意识到了这一点,但是我面对的问题似乎与使用线程池的策略没有任何关系,因为我确保了最少的线程数足以运行所有的线程控制器和下载5个任务。无论如何,我会采取你的建议,并使用2个独立的线程池来运行控制器和下载程序,并取消当前线程中的控制器。 – neevek 2011-12-26 10:38:32