ExecutorServiceの送信とExecutorServiceの実行のどちらかを選択します


193

戻り値が気にならない場合、ExecutorServiceの submitまたはexecute どちらを選択すればよいですか?

両方をテストした場合、戻り値以外は2つの間に違いはありませんでした。

ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.execute(new Task());

ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.submit(new Task());

回答:


203

例外/エラー処理に関して違いがあります。

キューに入れられたタスクがexecute()いくつかを生成するThrowableUncaughtExceptionHandlerThread実行中のタスクが呼び出されます。カスタムハンドラーがインストールされていない場合、UncaughtExceptionHandler通常はThrowableスタックトレースをに出力するデフォルトのSystem.errが呼び出されます。

一方、でThrowableキューに入れられたタスクによって生成されたsubmit()は、ThrowableFutureの呼び出しから生成されたにをバインドしsubmit()ます。呼び出すget()ことにしてFutureスローされますExecutionException、元にThrowable(呼び出すことでアクセス可能その原因としてgetCause()ExecutionException)。


19
この動作は、制御できない可能性のあるにRunnableラップされるかどうかに依存するため、保証されないことに注意してくださいTask。たとえば、Executor実際にであるScheduledExecutorService場合、タスクは内部でラップされ、FutureキャッチされなかっThrowableたはこのオブジェクトにバインドされます。
rxg 2013年

4
Futureもちろん、「に包まれているかどうか」という意味です。たとえば、ScheduledThreadPoolExecutor#executeの Javadocを参照してください。
rxg 2013年

59

execute:呼び出しに使用し、呼び出しを忘れます

submit:これを使用して、メソッド呼び出しの結果を検査Futureし、呼び出しによって返されたオブジェクトに対して適切なアクションを実行します

以下からのjavadoc

submit(Callable<T> task)

値を返すタスクを実行のために送信し、タスクの保留中の結果を表すFutureを返します。

Future<?> submit(Runnable task)

Runnableタスクを送信して実行し、そのタスクを表すFutureを返します。

void execute(Runnable command)

指定されたコマンドを将来のある時点で実行します。コマンドは、Executor実装の裁量で、新しいスレッド、プールされたスレッド、または呼び出しスレッドで実行できます。

の使用中は注意が必要submit()です。タスクコードをtry{} catch{}ブロックに埋め込まない限り、フレームワーク自体に例外を隠します。

コード例:このコードはを飲み込みArithmetic exception : / by zeroます。

import java.util.concurrent.*;
import java.util.*;

public class ExecuteSubmitDemo{
    public ExecuteSubmitDemo()
    {
        System.out.println("creating service");
        ExecutorService service = Executors.newFixedThreadPool(10);
        //ExtendedExecutor service = new ExtendedExecutor();
        service.submit(new Runnable(){
                 public void run(){
                    int a=4, b = 0;
                    System.out.println("a and b="+a+":"+b);
                    System.out.println("a/b:"+(a/b));
                    System.out.println("Thread Name in Runnable after divide by zero:"+Thread.currentThread().getName());
                 }
            });
        service.shutdown();
    }
    public static void main(String args[]){
        ExecuteSubmitDemo demo = new ExecuteSubmitDemo();
    }
}

出力:

java ExecuteSubmitDemo
creating service
a and b=4:0

同じコードは()で置き換えることsubmit()でスローされますexecute

交換する

service.submit(new Runnable(){

service.execute(new Runnable(){

出力:

java ExecuteSubmitDemo
creating service
a and b=4:0
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
        at ExecuteSubmitDemo$1.run(ExecuteSubmitDemo.java:14)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:744)

submit()の使用中にこれらのタイプのシナリオを処理するにはどうすればよいですか?

  1. タスクコード(RunnableまたはCallable実装のいずれか)をtry {} catch {}ブロックコードで埋め込む
  2. 実装する CustomThreadPoolExecutor

新しいソリューション:

import java.util.concurrent.*;
import java.util.*;

public class ExecuteSubmitDemo{
    public ExecuteSubmitDemo()
    {
        System.out.println("creating service");
        //ExecutorService service = Executors.newFixedThreadPool(10);
        ExtendedExecutor service = new ExtendedExecutor();
        service.submit(new Runnable(){
                 public void run(){
                    int a=4, b = 0;
                    System.out.println("a and b="+a+":"+b);
                    System.out.println("a/b:"+(a/b));
                    System.out.println("Thread Name in Runnable after divide by zero:"+Thread.currentThread().getName());
                 }
            });
        service.shutdown();
    }
    public static void main(String args[]){
        ExecuteSubmitDemo demo = new ExecuteSubmitDemo();
    }
}

class ExtendedExecutor extends ThreadPoolExecutor {

   public ExtendedExecutor() { 
       super(1,1,60,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(100));
   }
   // ...
   protected void afterExecute(Runnable r, Throwable t) {
     super.afterExecute(r, t);
     if (t == null && r instanceof Future<?>) {
       try {
         Object result = ((Future<?>) r).get();
       } catch (CancellationException ce) {
           t = ce;
       } catch (ExecutionException ee) {
           t = ee.getCause();
       } catch (InterruptedException ie) {
           Thread.currentThread().interrupt(); // ignore/reset
       }
     }
     if (t != null)
       System.out.println(t);
   }
 }

出力:

java ExecuteSubmitDemo
creating service
a and b=4:0
java.lang.ArithmeticException: / by zero

良いパリッとした説明。拡張することはできますが、実際には必要ありません。タスクが成功したかどうかを知るには、その将来のオブジェクトを消費する必要があります。したがって、Future <t>を使用する予定の場合は、submit()を使用します。それ以外の場合は、execute()を使用します
prash

11

戻り値の型を気にしない場合は、executeを使用してください。これはsubmitと同じですが、Futureが返されることはありません。


15
受け入れられた回答によると、これは正しくありません。例外処理はかなり重要な違いです。
Zero3、2015年

7

Javadocから取得:

メソッドsubmitは、ベースメソッド{@link Executor#execute}を拡張して、実行をキャンセルしたり、完了を待機したりするために使用できる{@link Future}を作成して返します。

これは実際には個人的な好みの問題ですが、個人的には、宣言的な感じがするため、実行の使用を好みます。

詳細については、ExecutorService実装の場合、への呼び出しによって返されるコア実装Executors.newSingleThreadedExecutor()ThreadPoolExecutorです。

submit呼び出しは、その親によって提供されAbstractExecutorService、すべてのコールは内部で実行されます。executeは、directによってオーバーライド/提供されThreadPoolExecutorます。


2

Javadocから:

コマンドは、Executor実装の裁量で、新しいスレッド、プールされたスレッド、または呼び出しスレッドで実行できます。

したがって、実装によってExecutorは、タスクの実行中に送信スレッドがブロックする場合があります。


1

完全な回答は、ここで公開された2つの回答の構成です(少し「追加」)。

  • タスクを送信する(それを実行するのではない)と、結果を取得したり、アクションをキャンセルしたりするために使用できるfutureが返されます。あなたはあなたにこの種の制御はありませんexecute(その戻り値の型idのためvoid
  • executeRunnablewhile submitはa RunnableまたはaのいずれかをCallable引数として取ることができると想定しています(2つの違いの詳細については、以下を参照してください)。
  • execute未チェックの例外をすぐにバブルします(チェック済みの例外をスローすることはできません!!!)一方で、あらゆる種類の例外を結果として返されるフューチャーにsubmitバインドし、(ラップされた)例外を呼び出したときにのみスローされます。取得するThrowableはのインスタンスであり、このオブジェクトを呼び出すと、元のThrowableを返します。future.get()ExecutionExceptiongetCause()

さらにいくつかの(関連する)ポイント:

  • submit必要なタスクが結果を返す必要がない場合でも、Callable<Void>Runnable
  • タスクのキャンセルは、割り込みメカニズムを使用して行うことができます。ここだキャンセルポリシーを実装する方法のは、

まとめるsubmitと、aとCallable(対)を使用することをお勧めexecuteRunnableます。そして、私はブライアン・ゲッツ著「Javaの並行性」から引用します:

6.3.2結果を伴うタスク:呼び出し可能および将来

Executorフレームワークは、基本的なタスク表現としてRunnableを使用します。Runnableは、かなり限定的な抽象化です。runは、値を返したり、チェックされた例外をスローしたりすることはできませんが、ログファイルへの書き込みや共有データ構造への結果の配置などの副作用が生じる可能性があります。多くのタスクは、データベースクエリの実行、ネットワーク経由でのリソースのフェッチ、複雑な関数の計算など、効果的に据え置かれた計算です。これらのタイプのタスクでは、Callableの方が優れた抽象化です。Callableは、メインエントリポイントであるcallが値を返し、例外がスローされることを予期します。およびjava.security.PrivilegedActionとCallable。


1

受け入れられた答えに追加するだけ

ただし、タスクからスローされた例外は、execute()でサブミットされたタスクに対してのみ、キャッチされない例外ハンドラーになります。submit()を使用してexecutorサービスに送信されたタスクの場合、スローされた例外はすべて、タスクの戻り状況の一部と見なされます。

ソース

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.