プログラミング原人の進化ログ

プログラミング原人の進化論

オレ プログラミング ベンキョウ スル。マナンダ コト カク。

【Java】スレッドの基本

Javaのスレッドについて基本的なことをまとめておきます。

スレッド

Javaでは複数の処理を同時に並行して行うことができます。それぞれの処理の分岐のことをスレッドと言います。

スレッドの作り方

処理を分岐させてスレッドを作る方法は2つあります。

スレッドの作り方1

1つ目は、クラスライブラリのThreadクラスを継承したクラスを作り、そこに別スレッドで行いたい処理を書く方法です。 Threadクラスのサブクラスでrun()メソッドを定義すると、その内容がスレッドを新しく起動した時に行われる処理となります。

class クラス名 extends Thread {
  public void run() {
  // 新しく起動したスレッドで行いたい処理
  }
  ...
}

このクラスのインスタンスを作成し、start()メソッドを適用することによって、新しくスレッドが起動します。

例を見てみます。

Hello1.java

class Hello1 extends Thread {
  private String name;

  public Hello1(String name) {
    this.name = name;
  }
  
  public void run() {
    for(int i = 0; i < 3; i++) {
      System.out.println("Hello, " + this.name);
    }
  }
}

Example1.java

class Example1 {
  public static void main(String[] args) {
    Hello1 hello = new Hello1("World");

    hello.start();  // 新しいスレッド起動

    for (int i = 0; i < 3; i++) {
      System.out.println("Main");
    }
  }
}

この2つを同じディレクトリに保存し、実行すると次のようになりました。

Main
Main
Main
Hello, World
Hello, World
Hello, World

なんか、あんまり並列処理している感じがないですが...。実行環境によって出力される順番は変わると思います。

スレッドの作り方2

2つ目は、クラスライブラリのRunnableインターフェースを実装するクラスを作る方法です。 この場合はThreadクラスのインスタンスを作成してそれに対してstart()メソッドを適用することになります。

例を見てみます。

Hello2.java

class Hello2 implements Runnable {
  private String name;

  public Hello2(String name) {
    this.name = name;
  }
  
  public void run() {
    for(int i = 0; i < 3; i++) {
      System.out.println("Hello, " + this.name);
    }
  }
}

Example2.java

class Example2 {
  public static void main(String[] args) {
    Hello2 hello = new Hello2("World");
    Thread th = new Thread(hello);

    th.start();  // 新しいスレッド起動

    for (int i = 0; i < 3; i++) {
      System.out.println("Main");
    }
  }
}

実行結果:

Main
Main
Main
Hello, World
Hello, World
Hello, World

スレッドを一時停止する

sleep()メソッドを使います。オブジェクト.sleep(x)とすることで処理がxミリ秒だけ停止します。

次の例はHello1.javaExample1.javaのうち、Hello1.javaのrun()メソッドだけを書き換えたものです。エラー処理を書かないとコンパイルする時に怒られます。

Hello3.java

class Hello3 extends Thread {
  private String name;

  public Hello3(String name) {
    this.name = name;
  }
  
  public void run() {
    for(int i = 0; i < 3; i++) {
      try {
        this.sleep(1000);
        System.out.println("Hello, " + this.name);
      }
      catch(InterruptedException e) {}
    }
  }
}

Example3.java

class Example3 {
  public static void main(String[] args) {
    Hello3 hello = new Hello3("World");

    hello.start();  // 新しいスレッド起動

    for (int i = 0; i < 3; i++) {
      System.out.println("Main");
    }
  }
}

実行結果:

Main
Main
Main
Hello, World
Hello, World
Hello, World

それぞれのHello, Worldが表示されるまでに1000ミリ秒=1秒が経過します。

他のスレッドが終了するまで待機する

join()メソッドを使います。あるオブジェクトに対してjoinメソッドを用いると、そのオブジェクトに関連づけられたスレッドが終了するまで待機し、それから以降の処理を行います。

Example1を改造した例です。

Hello4.java

class Hello4 extends Thread {
  private String name;

  public Hello4(String name) {
    this.name = name;
  }
  
  public void run() {
    for(int i = 0; i < 3; i++) {
      System.out.println("Hello, " + this.name);
    }
  }
}

Example4.java

class Example4 {
  public static void main(String[] args) {
    Hello4 hello = new Hello4("World");

    hello.start();  // 新しいスレッド起動

    // hello に関連づけられたスレッドが終了するまで待機
    try {
      hello.join();
    }
    catch (InterruptedException e) {}

    for (int i = 0; i < 3; i++) {
      System.out.println("Main");
    }
  }
}

実行結果:

Hello, World
Hello, World
Hello, World
Main
Main
Main

スレッドを同期させる

複数のスレッドが同じあるメソッドを呼び出す時、メソッドの呼び出しを同時に行わないようにすることを同期と言います。各スレッドは順番にそのメソッドの呼び出しを行うことになります。
そのようなメソッドを作りたいときは、メソッドの宣言時にsynchronizedとつけます。

アクセス指定子 synchronized 戻り値の型 メソッド名() {
  …
}

参考

高橋麻奈(2019) 「やさしいJavaSBクリエイティブ株式会社