4

時間制限のあるバックグラウンドで作業をしたいのですが。問題は、メインスレッドをブロックしたくないということです。

ナイーブな実装は、2つのエグゼキュータサービスを持つことです。1つはスケジューリング/タイムアウト用で、もう1つは作業の実行を担当します。

final ExecutorService backgroundExecutor = Executors.newSingleThreadExecutor();
final ExecutorService workerExecutor = Executors.newCachedThreadExecutor();


backgroundExecutor.execute(new Runnable() {
    public void run() {
        Future future = workerExecutor.submit(new Runnable() {
            public void run() {
                // do work
            }
        });
        try {
            future.get(120 * 1000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            logger.error("InterruptedException while notifyTransactionStateChangeListeners()", e);
            future.cancel(true);
        } catch (ExecutionException e) {
            logger.error("ExecutionException", e);
        } catch (TimeoutException e) {
            logger.error("TimeoutException", e);
            future.cancel(true);
        }
    }
});

他に解決策はありますか?

4

2 に答える 2

2

そのように単一のスレッドを 1 回実行するためだけに ExecutorService は必要ありません。代わりに FutureTask を作成すると、オーバーヘッドなしで同じ利点が得られます。

FutureTask<T> future = new FutureTask<T>(callable);
Thread thread = new Thread(future);
thread.start();
try {
    future.get(120 * 1000, TimeUnit.MILLISECONDS);
} ...

上記のスニペットの callable があなたの仕事です。Runnable がある場合 (上記のコード ブロックで行っているように)、次の方法で Callable に変換できます。

Callable callable = Executors.callable(runnable, null);

要約すると、コードは次のように変更できます。

backgroundExecutor.execute(new Runnable() {
    public void run() {

        Runnable myRunnable = new Runnable() {
            public void run() {
                // do work
            }
        } 

        Callable callable = Executors.callable(myRunnable, null);

        FutureTask<T> future = new FutureTask<T>(callable);
        Thread thread = new Thread(future);
        thread.start();

        try {
            future.get(120 * 1000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            logger.error("InterruptedException while notifyTransactionStateChangeListeners()", e);
            future.cancel(true);
        } catch (ExecutionException e) {
            logger.error("ExecutionException", e);
        } catch (TimeoutException e) {
            logger.error("TimeoutException", e);
            future.cancel(true);
        } 
    }
});

エグゼキュータをシャットダウンするために finally は必要ありません。ただし、他のリソースをクリーンアップするために finally が必要な場合もあります。

于 2012-04-26T13:08:10.857 に答える