Javaで並行スレッドを設計するときにRunnable
インターフェースとCallable
インターフェースを使用することの違いは何ですか?なぜ、どちらを選択するのですか?
説明 はこちら をご覧ください。
CallableインタフェースはRunnableと似ていますが、どちらもインスタンスが別のスレッドによって実行される可能性があるクラス用に設計されています。 ただし、Runnableは結果を返さず、チェック済み例外をスローすることはできません。
Runnable
とCallable
のアプリケーションの違いは何ですか。違いはCallable
に存在するreturnパラメータとの違いだけですか?
基本的には、そうです。 この質問 の答えを見てください。そして javadoc for Callable
。
Callable
がRunnable
ができることすべてを実行できる場合、両方を持つ必要がありますか?
Runnable
インターフェースはCallable
が行うことすべてを行うことができないため、できません。
Runnable
はJava 1.0から登場しましたが、Callable
はJava 1.5でのみ導入されました... Runnable
がサポートしないユースケースを処理するために。理論的には、JavaチームはRunnable.run()
メソッドのシグネチャを変更できたかもしれませんが、これは1.5より前のコードとのバイナリ互換性を壊し、古いJavaコードを新しいJVMに移行するときに再コーディングを必要とします。それは大きなNOです。 Javaは後方互換性を保つように努力しています...そしてそれはビジネスコンピューティングのためのJavaの最大のセールスポイントの1つでした。
そして、明らかに、タスクがneedで結果を返さなかったり、チェックされた例外を投げたりしないユースケースがあります。そのようなユースケースでは、Runnable
を使用する方がCallable<Void>
を使用してcall()
メソッドからダミー(null
)値を返すよりも簡潔です。
Callable
はcall()
メソッドを実装する必要がありますが、Runnable
はrun()
メソッドを実装する必要があります。Callable
は値を返すことができますが、Runnable
は返すことができません。Callable
はチェック例外をスローできますが、Runnable
はスローできません。Callable
はExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)
メソッドで使用できますが、Runnable
は使用できません。
public interface Runnable {
void run();
}
public interface Callable<V> {
V call() throws Exception;
}
私はこれをもう少し詳しく説明できる別のブログで見つけました 違い :
どちらのインターフェースも異なる実行スレッドで実行したいクラスによって実装されていますが、2つのインターフェースには次のような違いはほとんどありません。
Callable<V>
インスタンスはV
型の結果を返しますが、Runnable
インスタンスは返しません。Callable<V>
インスタンスはチェックされた例外を投げますが、Runnable
インスタンスは投げません。Javaの設計者はRunnable
インターフェースの機能を拡張する必要性を感じましたが、彼らはRunnable
インターフェースの使用に影響を与えたくなかったので、おそらくJava 1.5ではCallable
という名前のインターフェースを別に選んだ理由既存のRunnable
を変更します。
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を呼び出すとすぐに、他のすべてのキュー項目が実行されるまでブロックする必要があります。そのとき初めて、メソッドは値を返します。これは同期メカニズムです。
Callable
インターフェースはcall()
メソッドを宣言しており、Objectの型としてジェネリックを提供する必要があります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
は、runnableを使ってThreadを作成し、それにstart()を呼び出すときに呼び出されるrun()
メソッドを宣言するインターフェースです。 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();
}
いくつかの注目すべき違いを要約すると、
Runnable
オブジェクトは結果を返しませんが、Callable
オブジェクトは結果を返します。Runnable
オブジェクトは、チェック済み例外をスローできません。ただし、Callable
オブジェクトは、例外をスローできます。Runnable
インターフェースはJava 1.0から登場しましたが、Callable
はJava 1.5でのみ導入されました。類似点はほとんどありません
ExecutorServiceインターフェースのメソッドは
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);
Oracleのマニュアルに記載されているこれらのインタフェースの目的
Runnable インタフェースは、そのインスタンスがThread
によって実行されることを意図しているすべてのクラスによって実装されるべきです。クラスはrun
と呼ばれる引数のないメソッドを定義しなければなりません。
呼び出し可能 :結果を返し、例外をスローする可能性があるタスク。実装者は、callと呼ばれる引数なしで単一のメソッドを定義します。 Callable
インターフェースは、インスタンスが別のスレッドによって実行される可能性があるクラス用に設計されているという点でRunnable
と似ています。ただし、Runnable
は結果を返さず、チェック済み例外をスローすることもできません。
その他の違い
スレッド を作成するためにRunnable
を渡すことができます。しかし、パラメータとして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();
}
}
Runnable
を発砲に使用し、呼び出しを忘れてください。結果を検証するにはCallable
を使用してください。
Callable
は、Runnable
とは異なり、 invokeAll メソッドに渡すことができます。メソッドinvokeAny
およびinvokeAll
は、最も一般的に有用な形式の一括実行を実行し、一連のタスクを実行してから、少なくとも1つまたはすべてが完了するのを待ちます。
ちょっとした違い:実装するメソッド名=> Runnable
の場合はrun()
、Callable
の場合はcall()
。
すでに述べたように、Callableは比較的新しいインターフェースであり、並行処理パッケージの一部として導入されました。 CallableとRunnableはどちらもexecutorで使用できます。 Threadクラス(Runnable自体を実装する)は、Runnableのみをサポートします。
Runnableをエクゼキュータと共に使用することはできます。 Callableの利点は、実行プログラムに送信してすぐに元に戻すことができるということです。これは、実行が完了すると更新されます。同じことがRunnableで実装されるかもしれません、しかしこの場合あなたは結果をあなた自身で管理しなければなりません。たとえば、すべての結果を保持する結果キューを作成できます。他のスレッドはこのキューで待機し、到着した結果を処理できます。
+-------------------------------------+--------------------------------------------------------------------------------------------------+
| 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
インターフェースの使用には影響を与えたくなかったので、おそらくJava 1.5でCallable
という名前のインターフェースを別に選んだ理由です。 Java 1.0以降Javaの一部であった既存のRunnable
インタフェースを変更する。 ソース
CallableとRunnableの違いは次のとおりです。
CallableとRunnableの両方は互いに似ており、スレッドの実装に使用できます。 Runnableを実装する場合はrun()メソッドを実装する必要がありますが、呼び出し可能の場合はcall()メソッド、両方のメソッドを実装する必要があります同様の方法で動作しますが、呼び出し可能call()メソッドには柔軟性があります。それらにはいくつかの違いがあります。
Runnableとcallableの違いは以下のとおりです-
1)run()runnableのメソッドはvoidを返します。つまり、スレッドが何かを返したい場合は、さらに使用できるものを返します- Runnable run()での選択なしメソッド。解決策があります'Callable'、objectの形式で何かを返したい場合、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.バージョンですが、callableはJava 1.5実行者フレームワーク。
Executersに精通している場合は、RunnableではなくCallableを使用にする必要があります。
ご理解ください。
Runnable(vs)Callableは、Executerフレームワークを使用しているときに有効になります。
ExecutorServiceは、 Executor
のサブインターフェースで、RunnableタスクとCallableタスクの両方を受け入れます。
以前のマルチスレッドは、InterfaceRunnable
を使って実現できます。1.0以降しかし、ここで問題はスレッドタスクを完了した後に我々はスレッド情報を収集することができないということです。データを収集するために、静的フィールドを使用することができます。
例各生徒データを収集するために別々のスレッドを作成します。
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() );
}
この問題を解決するために彼らは導入しましたCallable<V>
1.5以降 これは結果を返し、例外を投げます。
単一の抽象メソッド:CallableおよびRunnableインターフェースはどちらも単一の抽象メソッドを持っています。つまり、それらはJava 8のラムダ式で使用できます。
public interface Runnable {
public void run();
}
public interface Callable<Object> {
public Object call() throws Exception;
}
実行用タスクを ExecutorService に委任する方法はいくつかあります。
execute(Runnable task):void
は新しいスレッドを生成しますが、メインスレッドや呼び出し元のスレッドはブロックしません。submit(Callable<?>):Future<?>
、submit(Runnable):Future<?>
は新しいスレッドを作成し、メインスレッドをブロックします。Runnable、Executorフレームワークで呼び出し可能なインタフェースの使用例。
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();
}
}