2012-08-11 178 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