ほぷしぃ

Java言語入門 〜C言語を学んだ君へ〜

[第14回]マルチスレッド

[1] マルチスレッドとは

この回ではマルチスレッドについて学習しましょう。
今までやってきたプログラムは、シングルスレッドと呼ばれています。
何か処理を行いたい時には、順番に行われるように指示をしてきました。
しかし、以下のような状態になった場合にはシングルスレッドのままでは大丈夫でしょうか。

以下のような場面があったとします。
受付が1つしかない、病院でたくさんの人がやってきたとします。
そうなると前の人が終わるまで次の人は待つ必要があります。
これでは、後ろの人はたくさん待たされることになり、とても不満が出ることでしょう。
その対策として受付を複数設ければ1つしかない場合に比べてだいぶましになるはずです。
そう、これこそがまさにこれから学習するマルチスレッドなのです。

ところでスレッドとは一体何のことでしょうか。
スレッドとは、パソコンが処理を行う最小単位のことです。
タスクという言葉を聞いたことがあると思います。
スレッドはタスクよりもさらに小さな処理単位です。

では、マルチスレッドプログラムを体験していきましょう。
といってもいきなり始めては分かりにくいでしょうからまずはイメージを見てみましょう。
ある3つの処理があったとします。これをそれぞれ「A」、「B」、「C」とすることにします。

シングルスレッドのイメージ

シングルスレッド

まずは今まで学習してきたシングルスレッドから確認してみます。
A→B→Cとといった具合に順番通り処理をしていきます。

マルチスレッドのイメージ

画像

シングルスレッドでは順番の処理を実行してきました。
マルチスレッドでは、上記のように複数の処理を並行して行ってくれます。
同時に実行することにより効率よく処理ができるようになります。
便利なものには弊害があり、以下のような注意点があります。

マルチスレッドの注意点

今まで1つずつしかできなかった処理が複数同時に実行できるようになります。
とても便利な機能なのですが注意点がいくつかあります。

・ 実行順序に決まりはない
・ 実行のたびに変化する

複数のスレッド間で全く関係がない場合には問題はありません。
しかし、複数のスレッドが同じ変数にアクセスする場合には大変なことになります。
このような時は他の処理から影響を受けないように排他制御を行います。
排他制御については次のページでやります。

マルチスレッドの実現のために

通常パソコンは1つのCPUを所持しています。
1つのCPUにつき、1つの処理が原則です。
このままでは複数処理なんてすることができません。
では、どのようにしてマルチスレッドを実現しているのでしょうか。
そこで、マルチスレッドを実現するためにCPUは、ある方法をとっています。
CPUを短い時間間隔で実行する処理を切り替えているのです。
これを「時分割処理(タイムシェアリング)」といいます。
複数の処理を短い間隔で頻繁に切り替えることで、
まるで複数の処理を同時に実行するように見せているのです。

[2] マルチスレッドの方法1

まずはThreadクラスを用いたマルチスレッドを学んでいきましょう。
Threadクラスを使ったマルチスレッドの実行方法は以下のとおりとなります。

Threadクラス

1、Threadクラスを継承してサブクラスを作成
2、サブクラス内にrun()メソッドを記述(オーバーライド)
3、サブクラスをインスタンス化
4、オブジェクトのstart()メソッドで実行

上記4手順によりThreadクラスを使ったマルチスレッドが実現できます。

1つ目の手順はThreadクラスを継承してサブクラスを作成することです。

class TestThread extends Thread{…}

と書きます。(ここではTestThreadという名前のサブクラスを作成しました。)
継承をもし覚えていなかったら過去の回に戻り復習をしましょう。

次に、サブクラスの中にrun()メソッドを記述します。

public void run(){…}

です。このメソッド内に記述された処理がマルチスレッドを実行した時に行われます。

3つ目はサブクラスをインスタンス化することです。

TestThread Test1 = new TestThread();

その後、mainメソッド内でこのオブジェクトをstart()メソッドで実行します。
では、実際にプログラムで見てみましょう。

マルチスレッド(Thrad)プログラム

public class Java14_01 {
    public static void main(String args[]){
        TestThread tt1 = new TestThread("スレッド1");
        TestThread tt2 = new TestThread("スレッド2");
        
        tt1.start();
        tt2.start();
        
        try{
            for(int i = 0; i < 5; i++){
                System.out.println("メイン実行中");
                Thread.sleep(500);
            }
        }catch (InterruptedException e){
            System.out.println(e);
        }
        finally{
            System.out.println("メイン終了");
        }
    }
}

class TestThread extends Thread{
    private String ThreadName;
    
    TestThread(String name){
        this.ThreadName = name;
    }
    
    
    public void run(){
        try{
            for(int i = 0; i < 5; i++){
                System.out.println(ThreadName+"実行中");
                Thread.sleep(1000);
            }
        }catch (InterruptedException e){
            System.out.println(e);
        }
        finally{
            System.out.println(ThreadName+"終了");
        }
    }
}

実行結果

マルチスレッド実行結果

スレッドをメイン以外に2つ作成しています。
そして、メインと2つのスレッドを同時に実行しています。

例外処理の中に書いてある、sleep()メソッドは、スレッドの処理を指定した時間(ミリ秒)だけ待機させます。
Thread.sleep(時間)と記述します。
Threadクラスのメソッドです。
継承しているサブクラス(このプログラムでは、TestThrad)では、
Thread.sleep(時間)をsleep(時間)と書くこともできます。

[3] マルチスレッドの方法2

次にRunnableインタフェースを使ったマルチスレッドを学びましょう。
では、Threadの時と同じように手順を見ていきましょう。

Runnableインタフェース

1、implements でRunnableインタフェースを指定してクラスを作成
2、クラス内でrun()メソッドを実装
3、作成したクラスをインスタンス化
4、インスタンス化したクラスをThreadのコンストラクタの引数として渡す
5、start()メソッドで実行

Threadクラスとは違い、1つ余分な手順があります。
Runnableを実装したクラスをインスタンス化した後に そのオブジェクトをThreadのコンストラクタの引数として渡します。

TestRun tr = new TestRun();
Thread t = new Thread(tr);

では、実際にプログラムで見ていきましょう。

マルチスレッド(Runnable)プログラム

public class Java14_02{
    public static void main(String args[]){
        TestRun tr = new TestRun("サブスレッド");
        Thread t = new Thread(tr);
        
        t.start();
        
        try {
            for(int i = 0; i < 5; i++){
                System.out.println("メインスレッド実行中");
                Thread.sleep(500);
            }
        }catch (InterruptedException e){
            System.out.println(e);
        }finally{
            System.out.println("メイン終了");
        }
        
    }
}

class TestRun implements Runnable{
    private String ThreadName;
    
    TestRun(String name){
        this.ThreadName = name;
    }
    
    public void run(){
        try {
            for(int i = 0; i < 5; i++){
                System.out.println(ThreadName+"実行中");
                Thread.sleep(600);
            }
        }catch (InterruptedException ex){
            
        }finally{
            System.out.println(ThreadName + "終了");
        }
    }
}

実行結果

Runnableの実行結果

RunnableとThreadの動作は変わりません。
ここまででスレッドを使う上での基本的なことを学びました。

[4] ThreadとRunnableの使い分け

さて、動作が同じなので、どのように使い分ければよいのでしょうか。
使い分ける基準としては、

run()メソッドを実装するだけならば、「Runnable」
run()メソッド以外もオーバーライドするならば、「Thread」

と2つを使い分けるとよいでしょう。

これで、マルチスレッドのプログラムはできました。
しかし、このままではできないことがまだあります。
少しだけ説明がありました。そう、排他制御です。
それは何なのか。次で学びましょう。



第13回へ ページのトップへ 次のページへ