7

リソースを大量に消費するコードを並行して実行したい場合は、AFAIKのsubmiting Callable/ Runnabletoが最適です。ExecutorServiceしたがって、私のメソッド構造:

public class ServiceClass {
    protected final ExecutorService executorService = Executors.newCachedThreadPool();

    public Future<Result> getResult(Object params) {
        if (params == null) {
            return null; // In situations like this the method should fail
        }
        // Do other fast pre-processing stuff
        return executorService.submit(new CallProcessResult(params));
    }

    private class CallProcessResult implements Callable<Result> {
        private Object params;
        public CallProcessResult(Object params) {
            this.params = params;
        }
        @Override
        public Result call() throws Exception {
            // Compute result for given params
            // Failure may happen here too!
            return result;
        }
    }
}
public class Result {
    ...
}

上記のコードで、障害が発生する可能性のある2つのスポットをマークしました。エラー処理に使用できるオプションは、これら2つの場合ではまったく異なります。

タスクを送信する前に、無効なパラメータ、失敗する可能性のある高速前処理コードなどの問題が発生する可能性があります。

ここで失敗を示すいくつかの方法があります。

  1. 無効の場合は、すぐにnullparamsを返します。この場合、呼び出すたびにnullが返さgetResultれるかどうかを確認する必要があります。getResult
  2. 上記の代わりにチェック済みの例外をスローします。
  3. Future<Result>要求に応じてnullを返すaをインスタンス化しますget()。私はApacheCommonsでそれを行いConcurrentUtils.constantFuture(null)ます。getResultこの場合、私は常にnull以外の値を返すことを期待しますFuture<Result>。2番目のケースと一致しているので、このオプションの方が好きです。

タスクの実行中に、メモリ不足、破損したファイル、使用できないファイルなどの重大なエラーが発生する可能性があります。

  1. タスクの結果はオブジェクトであるため、私の場合のより良いオプションはnullを返すことだと思います。
  2. また、チェックされた例外をスローして処理することもできますThreadPoolExecutor.afterExecute(NiranjanBhatによって提案されたように)。JavaExecutorServiceタスクからの例外の処理を参照してください。

どちらがより良い習慣ですか(どちらの場合も)?

おそらく、これを行う別の方法や、使用する必要のあるデザインパターンがありますか?

4

2 に答える 2

11

タスク処理中に失敗した場合は、適切な例外をスローすることをお勧めします。エグゼキュータでこれに特別な処理を追加しないでください。何が起こるかというと、それはキャプチャされ、に保存されますFutureFuture'sメソッドが呼び出されると、getがスローされExecutionException、の呼び出し元はこれをget解凍して処理できます。これは基本的に、通常の例外処理がCallable/Futureパラダイムに置き換えられる方法です。これは次のようになります。

    Future<Result> futureResult = serviceClass.getResult("foo");

    try {
        Result result = futureResult.get();
        // do something with result
    }
    catch (ExecutionException ee) {
        Throwable e = ee.getCause();
        // do something with e
    }

の呼び出し元getはこのExecutionExceptionsの処理を行う必要があるため、送信中の失敗に対処するためにそれを利用できます。これを行うにFutureは、Apache Commonsのようなものを作成できconstantFutureますが、指定された値を返すのではなく、指定された例外をスローします。JDKにはそのようなものはないと思いますが、次のように書くのは簡単です(退屈な場合)。

public class FailedFuture<T> implements Future<T> {
    private final Throwable exception;

    public FailedFuture(Throwable exception) {
        this.exception = exception;
    }

    @Override
    public T get() throws ExecutionException {
        throw new ExecutionException(exception);
    }

    @Override
    public T get(long timeout, TimeUnit unit) throws ExecutionException {
        return get();
    }

    @Override public boolean cancel(boolean mayInterruptIfRunning) { return false; }
    @Override public boolean isCancelled() { return false; }
    @Override public boolean isDone() { return true; }
}

これはやや危険です。同期的に呼び出されるメソッド中に障害が発生し、非同期的に呼び出されるメソッド中に障害のように見えます。エラーを処理する負担を、実際にエラーを引き起こしたコードから、後で実行されるコードに移しています。それでも、すべての障害処理コードを1か所にまとめることができるということです。これを価値あるものにするのに十分な利点かもしれません。

于 2012-12-18T11:14:06.183 に答える
2

afterExecuteメソッドを使用できます。これはThreadPoolExecutorで定義されており、オーバーライドする必要があります。
このメソッドは、各タスクの実行が完了した後に呼び出されます。このコールバックメソッドでタスクインスタンスを取得します。タスクのいくつかの変数にエラーを記録し、このメソッドでアクセスできます。

于 2012-12-18T08:38:21.770 に答える