JavaのRunnableインターフェースとCallableインターフェースの違いを徹底解説
Javaにおける Runnable と Callable は、どちらも関数型インターフェース(Functional Interface)であり、これらを実装したクラスは別スレッド上で実行されることを前提に設計されています。しかし、両者には戻り値の有無や例外処理など、いくつかの重要な違いがあります。
スレッドの起動方法の違い
Runnable を使って新しいスレッドを起動する方法は主に2つあります。1つは Thread クラスを継承する方法、もう1つは Runnable インターフェースを実装する方法です。Runnable オブジェクトはそのまま Thread のコンストラクタに渡せるため、手軽にマルチスレッド処理を実装できます。
一方、Thread クラスには Callable を受け取るコンストラクタが用意されていません。そのため、Callable を使う場合は ExecutorService クラスを利用してタスクを登録・実行し、結果は Future オブジェクト経由で受け取ります。
RunnableとCallableの比較表
| No. | 項目 | Runnable | Callable |
|---|---|---|---|
| 1 | パッケージ | java.lang に属する | java.util.concurrent に属する |
| 2 | スレッド生成 | 引数として渡すことでスレッドを生成できる | 引数として渡してもスレッドは生成できない |
| 3 | 戻り値 | 値を返さない(void) | 処理結果を返すことができる |
| 4 | メソッド | run() メソッドを持つ | call() メソッドを持つ |
| 5 | 一括実行 | タスクの一括実行には使用できない | invokeAll() を呼び出すことでタスクの一括実行が可能 |
| 6 | 例外処理 | run() 内でチェック例外をスローできない | call() はチェック例外をスローできる |
Runnableのサンプルコード
以下は Runnable を実装し、Thread に渡して実行する基本的な例です。
public class RunnableExample implements Runnable {
public void run() {
System.out.println("Hello from a Runnable!");
}
public static void main(String args[]) {
(new Thread(new RunnableExample())).start();
}
}
Callableのサンプルコード
次は Callable を実装し、ExecutorService で実行して Future 経由で結果を受け取る例です。
import java.util.concurrent.Callable;
public class Task implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("In call");
String name = "test";
return name;
}
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Main {
public static void main(String args[]) throws InterruptedException, ExecutionException {
ExecutorService services = Executors.newSingleThreadExecutor();
Future<?> future = services.submit(new Task());
System.out.println("In Future Object: " + future.get());
}
}
使い分けのポイント
処理結果が必要ない単純な非同期処理であれば Runnable が適しています。一方、計算結果や処理結果を呼び出し元に返したい場合、あるいはチェック例外を適切に扱いたい場合は Callable を選択しましょう。また、複数タスクをまとめて実行したい場面では、Callable と invokeAll() を組み合わせることで効率的な一括実行が可能になります。
-
JavaのQueueインターフェースにおけるpeek()・poll()・remove()メソッドの違いを徹底解説
JavaのQueue(キュー)インターフェースは、処理を行う前にデータを一時的に保持することを目的としたコレクションです。キューはFIFO(First-In-First-Out:先入れ先出し)方式で動作し、最初に追加された要素が最初に取り出されます。Queueインターフェースには先頭要素を取得するためのメソッドが複数用意されており、それぞれ挙動が異なります。本記事では、peek()、poll()、remove()の3つのメソッドの違いを、サンプルコードと実行結果を交えて詳しく解説します。peek()メソッドpeek()メソッドは、キューの先頭にある要素を削除せずに返します。キューの状態は変化し
-
JavaでのArrayListとHashSetの違いを徹底解説!使い分けのポイントも紹介
HashSetとArrayListは、どちらもJavaコレクションフレームワークにおいて最も重要なクラスの一つです。それぞれ特徴が異なるため、用途に応じた適切な使い分けが求められます。本記事では、ArrayListとHashSetの主な違いを6つの観点から比較し、実際のサンプルコードとともにわかりやすく解説します。ArrayListとHashSetの違い一覧No.項目ArrayListHashSet1実装インターフェースListインターフェースを実装しています。Setインターフェースを実装しています。2内部構造内部的には配列(動的配列)を使って実装されています。内部的にはHashMapを使って