1

私のプログラムは多数のドキュメントを分析し、無限または非常に長いループを引き起こすページを取得することがあります。これは事前に分析することはできません。特定のページを強制終了して次のページに進みたい(問題のあるページの結果を破棄する)。私はこの ようなSOの答えを読んだことがありますJavaで一定時間後に実行を停止する方法は?そして、次のコードを書きました:

// 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
    }

およびランナブル:

    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()スレッドは継続するため、これは主要な問題ではありません。

さらに質問:予想外に長いループになると予想されるThread.currentThread().isInterrupted()すべての場所に挿入する必要がありますか?runActions()

4

3 に答える 3

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
    }
}
于 2012-08-05T12:36:26.410 に答える
2

ここでの回答で言及されていない何かがあります。スレッドから実行された I/O をキャンセルしたい場合、それを「キャンセル」して、実際の I/O がキャンセルされることを期待することはできません。基本的に、「タスク」で割り込み例外を処理し、それに応じて処理する必要があります。ソケット接続を閉じることもできます。スレッドを使用して実行されているタスクを「停止」するための小さなスニペットがあります。

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();
        }        
    }

}
于 2012-08-05T12:47:41.157 に答える
2

あなたのrunActions-method は、設定されているスレッドの中断状態に反応しないようです。join呼び出し後の後者の呼び出しにinterruptはタイムアウトがなく、スレッドが終了するまで無期限に待機tします。runActions-method内で中断状態を確認し、中断状態が設定されている ( Thread.interrupted()true を返す) 場合は、操作を終了することで対応する必要があります。

于 2012-08-05T12:39:16.593 に答える