2012-08-11 447 views
10

有没有办法使用ExecutorService来暂停/恢复特定线程?Java ExecutorService暂停/恢复特定线程

private static ExecutorService threadpool = Executors.newFixedThreadPool(5);

想象一下,我想停止线程至极作为id = 0(假定每个之一被分配一个增量ID直到线程池的大小达到)。

经过一段时间后,通过按下一个按钮,比方说,我想恢复该特定的线程,并将所有其他线程保留为当前状态,可暂停或恢复。

我在Java文档中发现了一个未完成版本的PausableThreadPoolExecutor。但它不适合我需要,因为它恢复了池中的所有线程。

如果没有办法通过ExecutorService的默认实现来完成它,任何人都可以指向我的Java实现来解决这个问题吗?

谢谢!

+0

你确定吗?给定id的线程执行一些随机选择的工作,所以你正在停止一些未知的工作。这是什么意思? – 2012-08-11 15:46:04

+0

想象一下下面的情况:你有一个下载管理器,在那个管理器上你可以停止并恢复下载。对于每一个你正在下载的东西,无关紧要,你想停止下载,无论你想要恢复。这是否使问题更清楚? – 2012-08-11 16:10:21

+0

更清晰,但意义不大。停止/恢复下载是关于业务逻辑的,而线程池则关于计算资源。通过添加/删除资源来控制逻辑是一个好主意恕我直言。 – 2012-08-11 20:00:19

回答

7

你在错误的轨道上。线程池拥有线程并通过与您的代码共享它们可能会搞砸了。
您应该关注将您的任务(传递给线程可取消/可中断),并且不直接与池拥有的线程进行交互。
此外,您不会不知道被你尝试中断线程时执行什么样的工作,所以我不明白为什么你有兴趣做这个

更新:
的正确方法取消您在线程池中提交的任务是通过Future执行程序返回的任务。
1)通过这种方式,你可以肯定地知道你实际上瞄准的任务试图取消
2)如果你的任务已经被设计成可以取消的话那么你的中途就是一半
3)不要使用标志指示取消,但使用Thread.currentThread().interrupt()代替

更新:

public class InterruptableTasks { 

    private static class InterruptableTask implements Runnable{ 
     Object o = new Object(); 
     private volatile boolean suspended = false; 

     public void suspend(){   
      suspended = true; 
     } 

     public void resume(){  
      suspended = false; 
      synchronized (o) { 
       o.notifyAll(); 
      } 
     } 


     @Override 
     public void run() { 

      while(!Thread.currentThread().isInterrupted()){ 
       if(!suspended){ 
        //Do work here  
       } 
       else{ 
        //Has been suspended 
        try {     
         while(suspended){ 
          synchronized(o){ 
           o.wait(); 
          }       
         }      
        } 
        catch (InterruptedException e) {      
        }    
       }       
      } 
      System.out.println("Cancelled");   
     } 

    } 

    /** 
    * @param args 
    * @throws InterruptedException 
    */ 
    public static void main(String[] args) throws InterruptedException { 
     ExecutorService threadPool = Executors.newCachedThreadPool(); 
     InterruptableTask task = new InterruptableTask(); 
     Map<Integer, InterruptableTask> tasks = new HashMap<Integer, InterruptableTask>(); 
     tasks.put(1, task); 
     //add the tasks and their ids 

     Future<?> f = threadPool.submit(task); 
     TimeUnit.SECONDS.sleep(2); 
     InterruptableTask theTask = tasks.get(1);//get task by id 
     theTask.suspend(); 
     TimeUnit.SECONDS.sleep(2); 
     theTask.resume(); 
     TimeUnit.SECONDS.sleep(4);     
     threadPool.shutdownNow();  
    } 
+0

你可以更具体一些,为我提供一个真正的实现或例子吗?我真的很感激,因为我在这个问题上挣扎了很长时间。 – 2012-08-11 15:43:55

+0

@RicardoSantos:但是你为什么不让你的任务可以取消呢?这是不是一个好主意,你的代码不直接与线程交互 – Cratylus 2012-08-11 15:44:52

+0

我同意建议任务取消。该OP写道“想象我想停止线程ID = 0”。这没什么意义,因为在任何时候你都不知道线程0在做什么。但是,谈论停止正在进行的一些工作是有意义的,因此建议取消任务。 – 2012-08-11 15:51:04

4

建议:类似于/而不是你所使用的标志,创建一个semaphore 1许可证(new Semaphore(1))FO您需要暂停/取消暂停的每项任务。在任务的工作周期开始时输入如下代码:

semaphore.acquire(); 
semaphore.release(); 

这会导致任务获取信号量许可并立即释放它。现在,如果要暂停线程(例如,按下按钮),请从另一个线程调用semaphore.acquire()。由于信号量现在有0个许可证,因此您的工作线程将在下一个周期开始时暂停,并等待您从另一个线程呼叫semaphore.release()

(该acquire()方法抛出InterruptedException,如果你的工作线程被等待时被中断。还有另一种方法acquireUninterruptibly(),这也试图获得许可,但没有得到打断。)

+0

当然,它的确行得通,但听起来更像是黑客攻击,这是一种比无所事事更好的方法。所以谢谢你的回答。我会测试你的方法和@ user384706,看看哪一个有更好的性能。 – 2012-08-12 17:18:59