概要
javaの初期の頃から、マルチスレッドは言語の主要な側面でした。 Runnableはマルチスレッドタスクを表現するために提供されるコアインタフェースであり、CallableはJava1.5で追加されたRunnableの改良版です。
この記事では、両方のインターフェイスの違いとアプリケーションについて説明します。
実行メカニズム
両方のインターフェイスは、複数のスレッドで実行できるタスクを表すように設計されています。 実行可能なタスクはThreadクラスまたはExecutorServiceを使用して実行できますが、呼び出し可能なタスクは後者を使用してのみ実行できます。
戻り値
これらのインターフェイスが戻り値を処理する方法をより深く見てみましょう。
3.1. Runnable
では、Runnableインターフェイスは機能的なインターフェイスであり、パラメータを受け入れず、値を返さない単一のrun()メソッドを持っています。
これは、受信イベントのログなど、スレッド実行の結果を探していない状況に適しています:
public interface Runnable { public void run();}
これを例で理解してみましょう:
public class EventLoggingTask implements Runnable{ private Logger logger = LoggerFactory.getLogger(EventLoggingTask.class); @Override public void run() { logger.info("Message"); }}
この例では、スレッドはキューからメッセージを読み取り、ログファイルに記録します。 タスクはExecutorServiceを使用して起動できます:
public void executeTask() { executorService = Executors.newSingleThreadExecutor(); Future future = executorService.submit(new EventLoggingTask()); executorService.shutdown();}
この場合、Futureオブジェクトは値を保持しません。
3.2. Callable
を使用すると、Callableインターフェイスは単一のcall()メソッドを含む汎用インターフェイスです。:
public interface Callable<V> { V call() throws Exception;}
数値の階乗の計算を見てみましょう:
public class FactorialTask implements Callable<Integer> { int number; // standard constructors public Integer call() throws InvalidParamaterException { int fact = 1; // ... for(int count = number; count > 1; count--) { fact = fact * count; } return fact; }}
call()メソッドの結果がFutureオブジェクト内で返されます:
@Testpublic void whenTaskSubmitted_ThenFutureResultObtained(){ FactorialTask task = new FactorialTask(5); Future<Integer> future = executorService.submit(task); assertEquals(120, future.get().intValue());}
例外処理
それらが例外処理にどのように適しているかを見てみましょう。
4.1. Runnable
では、メソッドシグネチャに”throws”句が指定されていないため、さらにチェックされた例外を伝播する方法はありません。
4.2. Callable
Callableのcall()メソッドには”throws Exception”句が含まれているため、チェックされた例外をさらに簡単に伝播できます:ExecutorServiceを使用してCallableを実行する場合、例外はFutureオブジェクトに収集され、futureを呼び出すことで確認できます。get()メソッド。 これにより、元の例外をラップするExecutionExceptionがスローされます:
@Test(expected = ExecutionException.class)public void whenException_ThenCallableThrowsIt() { FactorialCallableTask task = new FactorialCallableTask(-5); Future<Integer> future = executorService.submit(task); Integer result = future.get().intValue();}
上記のテストでは、無効な番号を渡しているため、ExecutionExceptionがスローされています。 この例外オブジェクトでgetCause()メソッドを呼び出して、元のチェックされた例外を取得できます。
Futureクラスのget()メソッドを呼び出さない場合、call()メソッドによってスローされた例外は報告されず、タスクは完了としてマークされます:
@Testpublic void whenException_ThenCallableDoesntThrowsItIfGetIsNotCalled(){ FactorialCallableTask task = new FactorialCallableTask(-5); Future<Integer> future = executorService.submit(task); assertEquals(false, future.isDone());}
上記のテストは、FactorialCallableTaskにパラメータの負の値に対して例外をスローした場合でも、正常に合格します。
結論
この記事では、RunnableインターフェイスとCallableインターフェイスの違いを検討しました。
いつものように、この記事の完全なコードはGitHubで入手できます。
Learn Springコースを通じて、Spring5とSpring Boot2を使い始めましょう:
>> コースをチェックしてください