マルチスレッドプログラミングの基本

マルチスレッドとは?

スレッド (thread) というのは平たく言えば、プログラムの処理の流れのことです。

マルチスレッドというのは、スレッドが複数ある状況です。つまり、複数の処理が同時に行われることになります。

Java でのマルチスレッドプログラミングの基本

Java でマルチスレッドにするにはまず、Runnable インターフェイスを実装した Runnable オブジェクトを作成します。これが処理単位になることから、 Runnable オブジェクトはタスクと言い換えられたりします。

Runnable インターフェイスは run メソッドを一つだけ持つ単純なインターフェイスです。

このタスク (Runnable オブジェクト) をスレッドオブジェクトに渡し、スレッドを開始すると、タスクの所定の処理が行われます。 これが Runnable インターフェイスの run メソッドの実行というわけです。

Java でスレッドを作成して実行する簡単な例

では早速、Java でスレッドを作成して実行する例を示します。

まず、Runnable インターフェイスを実装したクラスを定義します。

public class SimpleTask implements Runnable {
  public void run() {
    long t = Thread.currentThread().getId();
    System.out.printf("+++ Begin %d +++\n", t);
    try {
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.printf("+++ End %d +++\n", t);
  }
}

この run() メソッドが、これから作成するスレッド上で実行されます。

これを利用するテストプログラムは次の通りです。

public class TestApp {
  public static void main(String[] args) {
    long t = Thread.currentThread().getId();
    System.out.printf("--- Begin %d ---\n", t);
    try {
      Thread thread = new Thread(new SimpleTask());
      thread.start();
      thread.join();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.printf("--- End %d ---\n", t);
  }
}

この実行結果は次のようになります。

--- Begin 1 ---
+++ Begin 13 +++
+++ End 13 +++
--- End 1 ---

main 関数が始まるとまず --- Begin 1 --- というメッセージを出力しています。 ここで数字の 1 の部分はメインスレッドのスレッド ID です。

6行目でスレッドを作成しています。スレッド作成時に上で定義した SimpleTask のオブジェクトを渡しています。

このクラスは Runnable インターフェイスを実装しています。Runnnable インターフェイスは run() メソッドだけを定義します。 スレッド作成時に Runnable オブジェクトを渡すと、そのスレッドが実行を開始した時に run() が実行されます。

Thread の start メソッドで処理を開始します。これによって run() メソッドが新しく作成したスレッド上で実行されることになります。

上の実行例では +++ Begin 13 ++++++ End 13 +++ の部分は作成したスレッド内で出力しています。 13 はスレッドID です。

メインスレッドのスレッドIDは 1 でしたから、確かに異なるスレッドで実行されていることがわかります。

次に join メソッドを呼んでいます。これはそれを呼び出したスレッドが、 スレッドの完了を待つためのものです。

Java のマルチスレッド

join() メソッドを呼ぶことで、それを呼び出したスレッドがそのスレッドオブジェクトの終了を待つようになります。

逆に言えば join() を呼ばなければ、メインスレッドは別スレッドの終了を待ちません。

Java の匿名クラスで Runnable オブジェクトを定義する

上ではタスクを表すクラスを SimpleTask クラスとして独立させて定義しました。

こうした書き方のほか、次のように記述することも良く行われます。

public class TestApp {
  public static void main(String[] args) {
    long t = Thread.currentThread().getId();
    System.out.printf("--- Begin %d ---\n", t);
    try {
      Thread thread = new Thread(new Runnable() {
        public void run() {
          long t = Thread.currentThread().getId();
          System.out.printf("+++ Begin %d +++\n", t);
          try {
            Thread.sleep(3000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          System.out.printf("+++ End %d +++\n", t);
        }
      });
      thread.start();
      thread.join();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.printf("--- End %d ---\n", t);
  }
}

インターフェイス名をいきなり new する形式は、 匿名内部クラスといいます。

Java のラムダ式を利用した Runnable オブジェクトの作成

さらに、ラムダ式を使い次のように書くこともできます。

public class TestApp {
  public static void main(String[] args) {
    long t = Thread.currentThread().getId();
    System.out.printf("--- Begin %d ---\n", t);
    try {
      Thread thread = new Thread(() -> {
        long t1 = Thread.currentThread().getId();
        System.out.printf("+++ Begin %d +++\n", t1);
        try {
          Thread.sleep(3000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.printf("+++ End %d +++\n", t1);
      });
      thread.start();
      thread.join();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.printf("--- End %d ---\n", t);
  }
}

ラムダ式ではひとつだけのメソッドが定義されているインターフェイスを記述する時には、それを実行します。 このため Runnable インターフェイスは run() メソッドひとつなので、 run() と書かれていなくても動作します。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Java 入門