2012-08-05 60 views
1

我的程序分析了大量的文檔,偶爾會得到一個導致無限循環或非常長循環的頁面。這不能提前分析。我想殺掉特定頁面並繼續下一頁(扔掉違規頁面的任何結果)。我已經看了這麼回答such as thisHow to stop execution after a certain time in Java?並寫了下面的代碼:停止執行java線程和join()的作用

// main program 
    private void runThread() throws InterruptedException { 
     long timeout = 15000L; 
     RunPageAnalyzer runPageAnalyzer = new RunPageAnalyzer(this); 
     Thread t = new Thread(runPageAnalyzer); 
     long startTime = System.currentTimeMillis(); 
     t.start(); 
     while (t.isAlive()) { 
      t.join(1000); 
      long delta = System.currentTimeMillis() - startTime; 
      LOG.debug("delta: "+delta); 
      if (delta > timeout && t.isAlive()) { 
       t.interrupt(); 
       t.join; 
       break; 
      }   
     } 
    } 

在同一個類中的方法,通過線程

void runActions() { 
     // variable length calculation which should be abandoned if too long 
    } 

和Runnable接口叫:

class RunPageAnalyzer implements Runnable { 
    private PageAnalyzerAction pageAnalyzerAction; 
     public RunPageAnalyzer(PageAnalyzerAction pageAnalyzerAction) { 
      this.pageAnalyzerAction = pageAnalyzerAction; 
     } 

     public void run() { 
     try { 
      pageAnalyzerAction.runActions(); 
     } catch (Exception e) { 
      LOG.debug("Exception running thread ", e); 
     } 
    } 

的輸出正常結束runActions()似乎是OK:

=========== page 1 ============= 
13863 [main] DEBUG org.xmlcml.graphics.control.page.PageAnalyzerAction - pageActions: 24 on page 0 
14863 [main] DEBUG org.xmlcml.graphics.control.page.PageAnalyzerAction - delta: 1000 
15864 [main] DEBUG org.xmlcml.graphics.control.page.PageAnalyzerAction - delta: 2001 
16864 [main] DEBUG org.xmlcml.graphics.control.page.PageAnalyzerAction - delta: 3001 
16975 [main] DEBUG org.xmlcml.graphics.control.page.PageAnalyzerAction - delta: 3112 
16975 [main] DEBUG org.xmlcml.graphics.control.page.PageAnalyzerAction - finished page 

但是當超過時間限制時,進程掛起t.join()

=========== page 2 ============= 
16975 [main] DEBUG org.xmlcml.graphics.control.page.PageAnalyzerAction - pageActions: 24 on page 0 
17976 [main] DEBUG org.xmlcml.graphics.control.page.PageAnalyzerAction - delta: 1001 
18976 [main] DEBUG org.xmlcml.graphics.control.page.PageAnalyzerAction - delta: 2001 
// ... 
30976 [main] DEBUG org.xmlcml.graphics.control.page.PageAnalyzerAction - delta: 14001 
31976 [main] DEBUG org.xmlcml.graphics.control.page.PageAnalyzerAction - delta: 15001 

如果我省略了t.join()那麼過程表現爲我所期望的,但我很擔心,這可能只是建立線程就會出現問題後的巨大的數字。

更新: 到目前爲止的答案表明,這是非平凡的(我沒有找到標準的Java示例/教程非常有用)。關鍵是runActions()必須明確知道它可能會被中斷。 join()不是主要問題,因爲線程只是繼續前進。

進一步的問題: 我必須在所有runActions()我希望在不可預測的長期循環的位置插入Thread.currentThread().isInterrupted()

+1

是否有原因使用低級線程API而不是java.concurrent包? – assylias 2012-08-05 12:27:55

+0

是的。無知!請賜教 - 這就是我來到這裏的原因。 – 2012-08-05 12:29:03

+1

http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html – 2012-08-05 13:51:30

回答

3

我在這裏假設pageAnalyzerAction.runActions();可以被中斷(即它通過相當快地退出來處理中斷)。

如果你不舒服的低級別的線程API,你可以使用從java.concurrent包執行人及期貨來處理線程管理和超時政策對您:

  • ,執行程序用線程池處理線程管理,必要時重用它們
  • 在任務提交時返回的未來可以用超時查詢 - 如果任務在超時內未完成,未來將拋出TimeOutException,然後您可以取消你的任務

一個人爲的例子是:

//declare an executor somewhere in your code, at a high level to recycle threads 
ExecutorService executor = Executors.newFixedThreadPool(10); //number of threads: to be adjusted 

private void runThread() throws InterruptedException { 
    long timeout = 15000L; 
    RunPageAnalyzer runPageAnalyzer = new RunPageAnalyzer(this); 
    Future future = executor.submit(runPageAnalyzer); 
    try { 
     future.get(timeout, TimeUnit.MILLISECONDS); 
    } catch (ExecutionException e) { 
     //the runnable threw an exception: handle it 
    } catch (TimeoutException e) { 
     //the task could not complete before the timeout 
     future.cancel(true); //interrrupt it 
    } 
} 
+0

+1因反應迅速。我會嘗試你的答案。這是否意味着我必須測試runActions()中的中斷(請參閱@esaj) – 2012-08-05 12:46:09

+0

@ peter.murray.rust是 - 如果您不處理runActions()中的中斷,則無論您使用何種方法選擇。 – assylias 2012-08-05 12:47:09

+0

因此,假設我有像第三方庫函數提供的'number.factorize()'這樣的東西,它內部必須有isThreadInterrupted()?如果沒有,我什麼都做不了。 (我選擇了一個嚴重縮放的例子,這是不可預知的) – 2012-08-05 12:59:59

2

這聽起來像你runActions - 方法沒有反應到線程的中斷狀態被設定。後者join-呼叫interrupt後沒有超時,並且將無限期地等待線程t死亡。您應檢查runActions-方法中的中斷狀態,如果設置了中斷狀態(Thread.interrupted()返回true),則通過終止操作來做出反應。

+0

runActions-method對正在設置的線程的中斷狀態沒有反應,所以我必須在runActions()和文本中選擇一個循環(每次循環時都是這樣)我還不知道在哪裏尋找)+1看起來像是一個有用的起點 – 2012-08-05 12:45:14

2

還有其他的東西在這裏的答案沒有提到。如果你想取消從一個線程完成的I/O,你只是不能「取消」它,並期望實際的I/O被取消。你基本上必須處理你的「任務」中的中斷異常,並相應地處理它,甚至關閉套接字連接。我有一個small snippet致力於「停止」使用線程運行的任務,這些線程可能對您有幫助(如果它有錯別字,很長時間會寫回)。

public class ThreadStopTest { 

    public static void main(String[] args) { 
     testSqlThreadStop(); 
    } 


    private static void testSocketReadStop() { 
     ExecutorService executor = Executors.newFixedThreadPool(2); 
     SocketTask task = new SocketTask("http://www.yahoo.com", 80); 
     Future<Integer> future = executor.submit(task); 
     try { 
      Integer result = future.get(1, TimeUnit.SECONDS); 
      System.out.println("Computation complete; result: " + result); 
     } catch(TimeoutException te) { 
      future.cancel(true); 
      task.cleanupAfterCancel(); 
      System.out.println("Computation cancelled"); 
     } catch(Exception e) { 
      e.printStackTrace(); 
     } 
     executor.shutdown(); 
    } 

} 


class SocketTask implements CleanableTask<Integer> { 

    private final String host; 

    private final int port; 

    private Socket socket; 

    public SocketTask(final String host, final int port) { 
     this.host = host; 
     this.port = port; 
    } 

    @Override 
    public Integer call() throws Exception { 
     InputStream in = null; 
     // TODO: Actually update the count and cleanly handle exceptions 
     int bytesRead = 0; 
     try { 
      this.socket = new Socket(this.host, this.port); 
      in = this.socket.getInputStream(); 
      byte[] bytes = new byte[1000000]; 
      System.out.println("Started reading bytes"); 

      // The below behavior of waiting for a forceful close can be avoided 
      // if we modify the FutureTask class (the default Future impl) 
      // by passing in a CleanupHandler whose cleanup() method would be 
      // invoked after invoking the `cancel` method or by making all 
      // your tasks implement a CancelledTask interface which has a 
      // `cleanupAfterCancel` method to do the same. :) 
      try { 
       in.read(bytes); 
      } catch(SocketException se) { 
       if(Thread.currentThread().isInterrupted()) { 
        System.out.println("All OK; this socket was forcefully closed"); 
       } else { 
        se.printStackTrace(); // something was seriously wrong 
       } 
      } 
     } catch(Exception e) { 
      e.printStackTrace(); 
     } finally { 
      if(in != null) in.close(); 
     } 
     return Integer.valueOf(bytesRead); 
    } 

    @Override 
    public void cleanupAfterCancel() { 
     try { 
      this.socket.close(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     }   
    } 

} 
+0

+1對於另一個答案顯示他是一個不平凡的問題。 – 2012-08-05 13:04:10