1. 序章

Runnableを実装する必要がありますか、それとも Thread クラスを拡張する必要がありますか?」 よくある質問です。

この記事では、実際にはどのアプローチがより理にかなっているのか、そしてその理由を説明します。

2. スレッドを使用する

まず、Threadを拡張するSimpleThreadクラスを定義しましょう。

public class SimpleThread extends Thread {

    private String message;

    // standard logger, constructor

    @Override
    public void run() {
        log.info(message);
    }
}

このタイプのスレッドを実行する方法も見てみましょう。

@Test
public void givenAThread_whenRunIt_thenResult()
  throws Exception {
 
    Thread thread = new SimpleThread(
      "SimpleThread executed using Thread");
    thread.start();
    thread.join();
}

ExecutorServiceを使用してスレッドを実行することもできます。

@Test
public void givenAThread_whenSubmitToES_thenResult()
  throws Exception {
    
    executorService.submit(new SimpleThread(
      "SimpleThread executed using ExecutorService")).get();
}

これは、別のスレッドで単一のログ操作を実行するための非常に多くのコードです。

また、Javaは多重継承をサポートしていないため、SimpleThreadは他のクラスを拡張できないことに注意してください。

3. Runnableの実装

次に、 java.lang.Runnableインターフェイスを実装する簡単なタスクを作成しましょう。

class SimpleRunnable implements Runnable {
	
    private String message;
	
    // standard logger, constructor
    
    @Override
    public void run() {
        log.info(message);
    }
}

上記のSimpleRunnableは、別のスレッドで実行したいタスクです。

それを実行するために使用できるさまざまなアプローチがあります。 それらの1つは、Threadクラスを使用することです。

@Test
public void givenRunnable_whenRunIt_thenResult()
 throws Exception {
    Thread thread = new Thread(new SimpleRunnable(
      "SimpleRunnable executed using Thread"));
    thread.start();
    thread.join();
}

ExecutorServiceを使用することもできます。

@Test
public void givenARunnable_whenSubmitToES_thenResult()
 throws Exception {
    
    executorService.submit(new SimpleRunnable(
      "SimpleRunnable executed using ExecutorService")).get();
}

ExecutorService の詳細については、こちらをご覧ください。

現在、インターフェースを実装しているので、必要に応じて別の基本クラスを自由に拡張できます。

Java 8以降、単一の抽象メソッドを公開するインターフェースはすべて機能インターフェースとして扱われるため、有効なラムダ式のターゲットになります。

ラムダ式を使用して上記のRunnableコードを書き直すことができます

@Test
public void givenARunnableLambda_whenSubmitToES_thenResult() 
  throws Exception {
    
    executorService.submit(
      () -> log.info("Lambda runnable executed!"));
}

4. 実行可能またはスレッド

簡単に言えば、通常、スレッドではなく実行可能の使用をお勧めします。

  • Thread クラスを拡張する場合、そのメソッドをオーバーライドすることはありません。 代わりに、 Runnable( which Thread がたまたまを実装するメソッドをオーバーライドします。 これは、IS-Aスレッドの原則に明らかに違反しています。
  • Runnable の実装を作成し、それを Thread クラスに渡すと、継承ではなく構成が利用されます。これはより柔軟です。
  • Thread クラスを拡張した後、他のクラスを拡張することはできません
  • Java 8以降、Runnablesはラムダ式として表すことができます

5. 結論

このクイックチュートリアルでは、 Runnable の実装が、Threadクラスを拡張するよりも一般的に優れたアプローチであることを確認しました。

この投稿のコードは、GitHubにあります。