スレッドの実行

プログラムの中でスレッドを作成する方法は2つあります。 ・java.lang.Threadクラスを継承(extends)したクラスを作成する方法です。 ・java.lang.Runnableインタフェースを実装(implements)したクラスを作成する方法です。

2.1. Threadクラスを継承する方法

Threadクラスを継承したクラスを利用して新しいスレッドを実行するには、次のような手順を用います。

  • Threadクラスを継承するクラスを作成する。
  • そのクラスでrunメソッドを宣言する。
  • そのクラスのインスタンスを作成する。
  • そのインスタンスのstartメソッドを呼び出す。

この方法を用いたプログラムを見てみましょう。次のプログラムは、3から0まで数字をカウントダウンする処理を2つのスレッドで同時に実行するプログラムです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class CountDownThread extends Thread {

    private String name;

    public CountDownThread(String name) {
        this.name = name;
    }

    public void run() {
        for (int i = 3; i >= 0 ; i--) {
            try {
                sleep(1000);
            } catch (InterruptedException e) {}
            System.out.println(name + " : " + i);
        }
    }

    public static void main(String[] args) {
        CountDownThread t1 = new CountDownThread("thread 1");
        CountDownThread t2 = new CountDownThread("thread 2");
        t1.start();
        t2.start();
    }
}

1行目のクラス宣言を見ると、このクラスがThreadクラスを継承していることがわかります。

runメソッドには、新しいスレッドで実行させたい処理を記述します。ここではforループを用いてカウントダウン処理を行っています。なお、runメソッド内で使用しているsleepメソッドは、Threadクラスのstaticメソッドです。sleepメソッドは、現在実行中のスレッドを指定した時間だけ休止させる機能を持ちます。ここでは1回カウントするたびに1000ミリ秒(=1秒)待つように指定しています。

スレッドを利用するためには、スレッドのインスタンスを作成します。19〜20行目で2つのインスタンスを異なる名前で作成しています。

1
2
CountDownThread t1 = new CountDownThread("thread 1");
CountDownThread t2 = new CountDownThread("thread 2");

最後に、21〜22行目でstartメソッドを呼ぶことによりスレッドを起動しています。ここで、runメソッドではなく、startメソッドを呼んでいることに注意してください。runメソッドを呼んでもrunメソッドに記述した処理は実行されますが、通常のメソッド呼び出しと同じで、新しいスレッドは起動されません。startメソッドを呼ぶことによって、新しいスレッドが起動され、その新しいスレッドによってrunメソッドが実行されるのです

このプログラムを実行すると次のよう表示されます。作成した2つのスレッドが並行して動作しているのがわかります。

(実習課題1)


以下のプログラムを作成しなさい。

  • スレッドの動作スピードを競うコンソールプログラム。実装するスレッドは以下の2つ。
  • 与えられたint型の配列を選択ソートするスレッド。
  • 与えられたint型の配列をバブルソートするスレッド。
  • 2つのスレッドに与えられれるint型の配列は同じものとする。ただし必ずそれぞれのスレッドに対して1つずつ配列を作成する事。配列のサイズと中身のデータは任意とする。

2.2. Runnableインタフェースを実装する方法

Runnableインタフェースを実装したクラスを利用して新しいスレッドを実行するには、次の手順に従います。

  • Runnableインタフェースを実装したクラスを作成する。
  • そのクラスでrunメソッドを実装する。
  • そのクラスのインスタンスを作成する。
  • そのインスタンスを引数としたコンストラクタでThreadクラスのインスタンスを作成する。
  • そのThreadクラスのインスタンスのstartメソッドを呼び出す。

Threadクラスを継承する方法に比べて少しだけ手順が増えています。先ほどのカウントダウンプログラムをこちらの方法で書き換えてみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class CountDownTest implements Runnable {

    private String name;

    public CountDownTest(String name) {
        this.name = name;
    }

    public void run() {
        for (int i = 3; i >= 0 ; i--) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {}
            System.out.println(name + " : " + i);
        }
    }

    public static void main(String[] args) {
        CountDownTest cdt1 = new CountDownTest("thread 1");
        CountDownTest cdt2 = new CountDownTest("thread 2");
        Thread t1 = new Thread(cdt1);
        Thread t2 = new Thread(cdt2);
        t1.start();
        t2.start();
    }
}

Runnableインタフェースを実装するクラスで定義しなければいけないメソッドはrunメソッドだけです。runメソッドには、Threadクラスを継承する場合と同じく、新しいスレッドで実行したい処理を記述します。このプログラムのrunメソッドは、Threadクラスを継承した場合とほぼ同じですが、sleepメソッドがThreadクラスのメソッドであるため、その参照方法だけ変わっています。

Runnableインタフェースを実装したクラスを新しいスレッドで実行するには、Runnableインタフェースを実装したクラスのインスタンス生成に加えて、Threadクラスのインスタンスも作成しなければいけません。Threadクラスのインスタンスを作成するときに、コンストラクタの引数としてRunnableインタフェースを実装したクラスのインスタンスを指定します。スレッドの起動は、先ほどと同じようにThreadクラスのstartメソッドによって実現されます。スレッドの起動部分のプログラムを1つのスレッドのみに着目すれば、次のようになります。

1
2
3
    CountDownTest cdt1 = new CountDownTest("thread 1");
    Thread t1 = new Thread(cdt1);
    t1.start();

このプログラムの実行結果は、Threadクラスを継承した場合と同じようになります。

2.3. 2つのスレッド作成方法の比較

これらの2つのスレッド作成方法はどのように使い分ければよいのでしょうか。

Threadクラスを継承する方法のほうが、実現するのは簡単です。しかしrunメソッドをオーバーライドするだけの簡単なスレッドなら、Runnableインタフェースを利用するようにして下さい。Threadクラスを継承する方法は、run以外のメソッドもオーバーライドしてスレッドの基本的な動作を変えたい場合にのみ使用する事が推奨されています。

(実習課題2)


実習課題1のプログラムを改良しなさい。

「Thread」クラスを拡張している部分を、「Runnable」インタフェースを実装するように変える事。