JavaのRunnableインターフェースとCallableインターフェースの違い


492

Javaでコンカレントスレッドを設計するときにRunnableCallableインターフェースを使用する場合の違いは何ですか、なぜ一方を他方よりも選ぶのですか?


2
追加のディスカッションについては、このページを読んだ後、RunnableよりもCallableを優先する必要があるかを
バルフイン2013年

回答:


444

こちらの説明をご覧ください

CallableインターフェースはRunnableと似ていますが、どちらもインスタンスが別のスレッドによって実行される可能性があるクラス用に設計されています。ただし、Runnableは結果を返さず、チェック例外をスローできません。


270

用途の違いは何ですRunnableCallable。違いがあるのは、戻りパラメータだけCallableですか?

基本的にはい。この質問への回答をご覧ください。そしてのjavadocCallable

Callableそのすべてを行うことができる場合、両方を持つ必要性は何Runnableですか?

なぜなら、Runnableインターフェースはすべてのことを行うことができないからですCallable

RunnableはJava 1.0以降に存在しますがCallable、Java 1.5でのみ導入されました... Runnableサポートしていないユースケースを処理するためです。理論的には、JavaチームはRunnable.run()メソッドのシグネチャを変更できた可能性がありますが、これにより、1.5より前のコードとのバイナリ互換性が失われ、古いJavaコードを新しいJVMに移行するときに再コーディングが必要になります。それは大きな違いです。Javaは下位互換性を保つように努めています...そして、これはJavaのビジネスコンピューティングにおける最大のセールスポイントの1つです。

そして、明らかに、タスクが結果を返したり、チェックされた例外をスローする必要がないユースケースがあります。これらのユースケースでRunnableは、メソッドからCallable<Void>ダミー(null)値を使用して返すよりも、使用する方が簡潔ですcall()


9
どこからこの歴史を手に入れたのだろう。これは非常に便利です。
スパイダーマン2014年

4
@prash-基本的な事実は古い教科書にあります。一言で言えば、Javaの最初のエディションのようです。
スティーブンC

4
(@prash-また、Java 1.1の時代にJavaを使い始めることによって。)
Stephen C

1
@StephenC私があなたの答えを正しく読んだ場合Runnable、下位互換性の理由から(大部分)存在することを示唆しています。しかしCallable、インターフェースを(またはで)実装する(または要求する)ことが不必要または高すぎる状況はありませんScheduledFuture<?> ScheduledExecutorService.schedule(Runnable command, long delay, TimeUnit unit)か?歴史が現在の結果を強制しなかったとしても、言語で両方のインターフェースを維持することには利点がないのですか?
最大

1
@max-まあ、私はそれを言った、そして私はまだそれに同意する。ただし、これは2番目の理由です。しかし、それでも、互換性を維持する必要性がなければ変更Runnable れたのではないか思います。の「ボイラープレート」return null;は弱い議論です。(少なくとも、それは私の決定だったでしょう...後方互換性を無視できる架空のコンテキストでは。)
Stephen C

82
  • はメソッドCallableを実装する必要がありますが、call()メソッドを実装するRunnable必要がありますrun()
  • A Callableは値を返すことができますが、Runnableできません。
  • Aは、Callable例外を確認投げることができますが、Runnableできません。
  • Aは、Callable一緒に使用することができExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)た方法が、Runnableすることはできません。

    public interface Runnable {
        void run();
    }
    
    public interface Callable<V> {
        V call() throws Exception;
    }

17
ExecutorService.submit(実行可能なタスク)も存在し、非常に便利です
Yair Kukielka '29年

Runnableは、ExecutorServiceでも次の方法で使用できます。1)ExecutorService.execute(Runnable)2)ExecutorService.submit(Runnable)
Azam Khan

2
Executor.submit(Callable <T> task)もありますが、RunnableタスクCollection <?のコレクションでinvokeAllまたはinvokeAnyを実行することはできません。Callable <T >>タスクを拡張
nikli

36

私はこれをもう少しこれらの違いを説明できる別のブログで見つけました:

どちらのインターフェースも、異なる実行スレッドで実行したいクラスによって実装されますが、2つのインターフェースには次のような違いがあります。

  • Callable<V>インスタンスタイプの結果を返しV、一方、Runnableインスタンスはありません。
  • Callable<V>インスタンスは、一方で、チェック例外をスローする可能性Runnableのインスタンスができません

Javaの設計者は、Runnableインターフェースの機能を拡張する必要性を感じていましたが、インターフェースの使用に影響を与えたくなかっRunnableたためCallable、Java 1.5で名前が付けられた別のインターフェースを使用するようになったのは、すでに変更されている理由です。既存のRunnable


27

RunnableとCallableをどこで使用するかを見てみましょう。

RunnableとCallableはどちらも、呼び出しスレッドとは異なるスレッドで実行されます。ただし、Callableは値を返すことができ、Runnableはできません。これは実際にどこに当てはまりますか?

Runnable:火を忘れてタスクを忘れた場合は、Runnableを使用します。Runnable内にコードを配置し、run()メソッドが呼び出されると、タスクを実行できます。呼び出しスレッドは、タスクを実行するときに実際には気にしません。

Callable:タスクから値を取得しようとしている場合は、Callableを使用します。単独で呼び出すことができるようになりました。Callableをラップしてfuture.get()で値を取得するFutureが必要になります。ここで、呼び出しスレッドは、Futureが結果を返し、Callableのcall()メソッドが実行されるのを待っているまでブロックされます。

したがって、RunnableとCallableの両方のラップされたメソッドが定義されているターゲットクラスへのインターフェースについて考えてください。呼び出し側のクラスは、RunnableとCallableのどちらであるかがわからないインターフェースメソッドをランダムに呼び出します。Runnableメソッドは、Callableメソッドが呼び出されるまで非同期で実行されます。ここでは、ターゲットクラスから値を取得しているため、呼び出しクラスのスレッドがブロックされます。

注:ターゲットクラス内では、単一のスレッドエグゼキューターでCallableおよびRunnableを呼び出すことができるため、このメカニズムはシリアルディスパッチキューに似ています。したがって、呼び出し元がRunnableラップされたメソッドを呼び出す限り、呼び出しスレッドはブロックせずに本当に高速に実行されます。FutureメソッドでラップされたCallableを呼び出すとすぐに、他のすべてのキューに入れられたアイテムが実行されるまでブロックする必要があります。その後、メソッドは値を返します。これは同期メカニズムです。


14

Callableインターフェイスはcall()メソッドを宣言し、オブジェクトのタイプcall()が返す必要があるため、ジェネリックスを提供する必要があります-

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

Runnable一方run()、ランナブルを使用してスレッドを作成し、その上でstart()を呼び出すときに呼び出されるメソッドを宣言するインターフェースです。run()を直接呼び出すこともできますが、run()メソッドを実行するだけで同じスレッドになります。

public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used 
     * to create a thread, starting the thread causes the object's 
     * <code>run</code> method to be called in that separately executing 
     * thread. 
     * <p>
     * The general contract of the method <code>run</code> is that it may 
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

いくつかの注目すべき違いを要約すると

  1. Runnable一方、オブジェクトが結果を返さないCallableオブジェクトは結果を返します。
  2. Runnablewherasオブジェクトは、チェック例外をスローすることはできませんCallableオブジェクトが例外をスローすることができます。
  3. このRunnableインターフェースはJava 1.0以降Callableに存在しましたが、Java 1.5でのみ導入されました。

いくつかの類似点が含まれます

  1. RunnableまたはCallableインターフェースを実装するクラスのインスタンスは、別のスレッドによって実行される可能性があります。
  2. CallableインターフェースとRunnableインターフェースの両方のインスタンスは、submit()メソッドを介してExecutorServiceによって実行できます。
  3. どちらも関数型インターフェースであり、Java8以降のラムダ式で使用できます。

ExecutorServiceインターフェースのメソッドは次のとおりです。

<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);

13

Oracleドキュメントからのこれらのインターフェースの目的:

Runnableインターフェースは、インスタンスがによって実行されることを目的とするクラスによって実装される必要がありますThread。クラスは、という引数のないメソッドを定義する必要がありますrun

呼び出し可能:結果を返し、例外をスローするタスク。実装者は、callと呼ばれる引数のない単一のメソッドを定義します。Callableインタフェースは同様であるRunnableの両方がそのインスタンス潜在的に別のスレッドによって実行されるクラスのために設計されているという点で、。AはRunnable、しかし、結果を返さないとチェック例外をスローすることはできません。

その他の違い:

  1. を渡しRunnableThreadを作成できます。ただしCallable、パラメーターとして渡すことで新しいスレッドを作成することはできません。CallableはExecutorServiceインスタンスにのみ渡すことができます。

    例:

    public class HelloRunnable implements Runnable {
    
        public void run() {
            System.out.println("Hello from a thread!");
        }   
    
        public static void main(String args[]) {
            (new Thread(new HelloRunnable())).start();
        }
    
    }
  2. Runnable発砲や忘れ忘れの呼び出しに使用します。Callable結果を確認するために使用します。

  3. Callableとは異なり、invokeAllメソッドに渡すことができますRunnable。メソッドinvokeAnyおよびinvokeAll最も一般的に有用な形式の一括実行を実行し、タスクのコレクションを実行し、少なくとも1つまたはすべてが完了するのを待機します。

  4. わずかな違い:実装されるメソッド名=> run()for Runnableおよびcall()for Callable


11

ここですでに述べたように、Callableは比較的新しいインターフェースであり、並行性パッケージの一部として導入されました。CallableとRunnableの両方をエグゼキューターで使用できます。クラススレッド(Runnable自体を実装)は、Runnableのみをサポートします。

Runnableはエグゼキューターで引き続き使用できます。Callableの利点は、それをエグゼキューターに送信し、実行が完了すると更新されるFutureの結果をすぐに取得できることです。Runnableでも同じことが実装できますが、この場合は自分で結果を管理する必要があります。たとえば、すべての結果を保持する結果キューを作成できます。他のスレッドはこのキューで待機し、到着した結果を処理できます。


Javaで例外をスローするスレッドの例は何ですか?メインスレッドはその例外をキャッチできますか?そうでない場合、私はCallableを使用しません。アレックス、これについて何か洞察がありますか?ありがとう!
1兆

1
他のコードが例外をスローする可能性があるため、カスタムスレッドで実行されるコード。他のスレッドでそれをキャッチするには、カスタム通知メカニズム(リスナーに基づくなど)を使用するFutureか、すべての未処理の例外をキャッチするフックを使用または追加することにより、いくつかの作業を行う必要があります:docs.oracle.com/javase/6/docs/api/ java / lang /…
AlexR 2014年

素晴らしい情報!ありがとう、アレックス!:)
1兆

1
私はこの回答に賛成しました。なぜなら、それは(額面どおりに取れば正しく)呼び出し可能なオブジェクトでスレッドプールモデルを使用しなければならないことを表明しているからです。これについて明らかに残念なことThreadは、Callableインターフェイスを有効に活用するように拡張できないため、呼び出し可能なことや開発者が望む他のことを実行するように単一のスレッドをカスタマイズできることです。このコメントを読んだ人が私が間違っていると思った場合は、もっとよく知りたいと思います...

8
+-------------------------------------+--------------------------------------------------------------------------------------------------+
|              Runnable               |                                           Callable<T>                                            |
+-------------------------------------+--------------------------------------------------------------------------------------------------+
| Introduced in Java 1.0 of java.lang | Introduced in Java 1.5 of java.util.concurrent library                                           |
| Runnable cannot be parametrized     | Callable is a parametrized type whose type parameter indicates the return type of its run method |
| Runnable has run() method           | Callable has call() method                                                                       |
| Runnable.run() returns void         | Callable.call() returns a value of Type T                                                        |
| Can not throw Checked Exceptions    | Can throw Checked Exceptions                                                                     |
+-------------------------------------+--------------------------------------------------------------------------------------------------+

Javaの設計者は、Runnableインターフェースの機能を拡張する必要性を感じていましたが、インターフェースの使用に影響を与えたくなかっRunnableたためCallable、Java 1.5で名前が付けられた別のインターフェースを使用するようになったのは、すでに変更されている理由です。RunnableJava 1.0以降、Javaの一部となっている既存のインターフェース。ソース


7

CallableとRunnableの違いは次のとおりです。

  1. CallableはJDK 5.0で導入されましたが、RunnableはJDK 1.0で導入されました
  2. Callableにはcall()メソッドがありますが、Runnableにはrun()メソッドがあります。
  3. Callableには値を返すcallメソッドがありますが、Runnableには値を返さないrunメソッドがあります。
  4. callメソッドはチェック例外をスローできますが、runメソッドはチェック例外をスローできません。
  5. 呼び出し可能はsubmit()メソッドを使用してタスクキューに入れますが、実行可能はexecute()メソッドを使用してタスクキューに入れます。

RuntimeExceptionではなく、checked Exceptionを強調することが重要です
BertKing

5

CallableとRunnableはどちらも類似しており、スレッドの実装に使用できます。Runnableを実装する場合はrun()メソッドを実装する必要がありますが、callableの場合はcall()メソッドを実装する必要があります。どちらの方法も同じように機能しますが、呼び出し可能なcall()メソッドの方が柔軟性があります。これらにはいくつかの違いがあります。

のRunnable呼び出し可能 below--として

1)runnablerun()メソッドはvoidを返します。つまり、スレッドでさらに使用できるものをスレッドに返したい場合は、Runnable run()メソッドを使用できません「Callable」という解決策があります。オブジェクトの形で何かを返したい場合は、RunnableではなくCallableを使用する必要があります。呼び出し可能なインターフェースには、Objectを返すメソッド'call()'があります

メソッドの署名-Runnable->

public void run(){}

呼び出し可能->

public Object call(){}

2)Runnable run()メソッドの場合、チェックされた例外が発生すると、try catchブロック処理する必要がありますが、Callable call()メソッドの場合、チェックされた例外を以下のようにスローできます

 public Object call() throws Exception {}

3)RunnableはレガシーのJava 1.0バージョンからのものですが、CallableExecuterフレームワークを備えたJava 1.5バージョンからのものです。

Executersに慣れている場合は、RunnableではなくCallable使用する必要があります

ご理解いただければ幸いです。


2

Runnable(vs)Callableは、Executerフレームワークを使用しているときにポイントになります。

ExecutorServiceはのサブExecutorインターフェースであり、RunnableタスクとCallableタスクの両方を受け入れます。

以前のマルチスレッディングは、Interface 1.0以降を使用して実現できますが、ここで問題となるのは、スレッドタスクを完了した後、スレッド情報を収集できないことです。データを収集するために、静的フィールドを使用する場合があります。Runnable

例各生徒のデータを収集するための個別のスレッド。

static HashMap<String, List> multiTasksData = new HashMap();
public static void main(String[] args) {
    Thread t1 = new Thread( new RunnableImpl(1), "T1" );
    Thread t2 = new Thread( new RunnableImpl(2), "T2" );
    Thread t3 = new Thread( new RunnableImpl(3), "T3" );

    multiTasksData.put("T1", new ArrayList() ); // later get the value and update it.
    multiTasksData.put("T2", new ArrayList() );
    multiTasksData.put("T3", new ArrayList() );
}

この問題を解決するために、結果を返し、例外をスローする1.5以降のバージョンを導入しました。Callable<V>

  • 単一の抽象メソッド:CallableインターフェースとRunnableインターフェースの両方に単一の抽象メソッドがあります。つまり、Java 8のラムダ式で使用できます。

    public interface Runnable {
    public void run();
    }
    
    public interface Callable<Object> {
        public Object call() throws Exception;
    }

実行するタスクをExecutorServiceに委任する方法はいくつかあります。

  • execute(Runnable task):void このメソッドはvoidを返すため、新しいスレッドを作成しますが、メインスレッドまたは呼び出し元スレッドをブロックしません。
  • submit(Callable<?>):Future<?>future.get()submit(Runnable):Future<?>を使用している場合、新しいスレッドを作成し、メインスレッドをブロックします。

ExecutorフレームワークでのRunnable、Callableインターフェースの使用例。

class CallableTask implements Callable<Integer> {
    private int num = 0;
    public CallableTask(int num) {
        this.num = num;
    }
    @Override
    public Integer call() throws Exception {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " : Started Task...");

        for (int i = 0; i < 5; i++) {
            System.out.println(i + " : " + threadName + " : " + num);
            num = num + i;
            MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
        }
        System.out.println(threadName + " : Completed Task. Final Value : "+ num);

        return num;
    }
}
class RunnableTask implements Runnable {
    private int num = 0;
    public RunnableTask(int num) {
        this.num = num;
    }
    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " : Started Task...");

        for (int i = 0; i < 5; i++) {
            System.out.println(i + " : " + threadName + " : " + num);
            num = num + i;
            MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
        }
        System.out.println(threadName + " : Completed Task. Final Value : "+ num);
    }
}
public class MainThread_Wait_TillWorkerThreadsComplete {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        System.out.println("Main Thread start...");
        Instant start = java.time.Instant.now();

        runnableThreads();
        callableThreads();

        Instant end = java.time.Instant.now();
        Duration between = java.time.Duration.between(start, end);
        System.out.format("Time taken : %02d:%02d.%04d \n", between.toMinutes(), between.getSeconds(), between.toMillis()); 

        System.out.println("Main Thread completed...");
    }
    public static void runnableThreads() throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newFixedThreadPool(4);
        Future<?> f1 = executor.submit( new RunnableTask(5) );
        Future<?> f2 = executor.submit( new RunnableTask(2) );
        Future<?> f3 = executor.submit( new RunnableTask(1) );

        // Waits until pool-thread complete, return null upon successful completion.
        System.out.println("F1 : "+ f1.get());
        System.out.println("F2 : "+ f2.get());
        System.out.println("F3 : "+ f3.get());

        executor.shutdown();
    }
    public static void callableThreads() throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newFixedThreadPool(4);
        Future<Integer> f1 = executor.submit( new CallableTask(5) );
        Future<Integer> f2 = executor.submit( new CallableTask(2) );
        Future<Integer> f3 = executor.submit( new CallableTask(1) );

        // Waits until pool-thread complete, returns the result.
        System.out.println("F1 : "+ f1.get());
        System.out.println("F2 : "+ f2.get());
        System.out.println("F3 : "+ f3.get());

        executor.shutdown();
    }
}

0

関数型プログラミングと一致する一種のインターフェース命名規則です

//Runnable
interface Runnable {
    void run();
}

//Action - throws exception
interface Action {
    void run() throws Exception;
}

//Consumer - consumes a value/values, throws exception
interface Consumer1<T> {
    void accept(T t) throws Exception;
}

//Callable - return result, throws exception
interface Callable<R> {
    R call() throws Exception;
}

//Supplier - returns result, throws exception
interface Supplier<R> {
    R get() throws Exception;
}

//Predicate - consumes a value/values, returns true or false, throws exception
interface Predicate1<T> {
    boolean test(T t) throws Exception;
}

//Function - consumes a value/values, returns result, throws exception
public interface Function1<T, R> {
    R apply(T t) throws Throwable;
}

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