スレッド3

<概要>もっと!モット!スレッドを学びます。復習と補習とまとめです。

例題16-3-1 次のシングルスレッドプログラムをマルチスレッドに書き換えて下さい。

○シングルスレッドプログラム

class Job{
  int num;
  public Job(int n){
    num=n;
  }
  public void work(){
    System.out.println(this+" is working.");
    try{
      int n=(int)(Math.random()*10000);
      Thread.sleep(n);
    }catch(InterruptedException e){
      System.out.println(e);
    }
  }
  public String toString(){
    return "[Job "+num+"]";
  }
}


public class SingleThreadProgram{
  Job[] jobs;
  public SingleThreadProgram(int jobcount){
    jobs=new Job[jobcount];
    for(int i=0;i<jobcount;i++){
      jobs[i]=new Job(i);
    }
  }
  public void workAllJobs(){
    for(int i=0;i<jobs.length;i++){
      jobs[i].work();
    }
  }
  public static void main(String[] args){
    SingleThreadProgram self=new SingleThreadProgram(10);
    while(true){
      self.workAllJobs();
    }
  }
}


○実行結果(シングルスレッド)

……
[Job 0] is working.
[Job 1] is working.
[Job 2] is working.
[Job 3] is working.
[Job 4] is working.
[Job 5] is working.
[Job 6] is working.
[Job 7] is working.
[Job 8] is working.
[Job 9] is working.
[Job 0] is working.
[Job 1] is working.
[Job 2] is working.
……

○マルチスレッドプログラム1

class JobThread extends Thread{
  Job job;
  public JobThread(int n){
    job=new Job(n);
  }
  public void run(){
    while(true){
      job.work();
    }
  }
}

public class MultiThreadProgram1{
  public MultiThreadProgram1(int jobcount){
    for(int i=0;i<jobcount;i++){
      new JobThread(i).start();
    }
  }
  public static void main(String[] args){
    new MultiThreadProgram1(10);
  }
}


○実行結果(マルチスレッド1)

……
[Job 1] is working.
[Job 9] is working.
[Job 3] is working.
[Job 7] is working.
[Job 1] is working.
[Job 3] is working.
[Job 8] is working.
[Job 3] is working.
[Job 5] is working.
[Job 7] is working.
[Job 1] is working.
[Job 6] is working.
[Job 4] is working.
……

○マルチスレッドプログラム2

class RunnableJob extends Job implements Runnable{
  public RunnableJob(int n){
    super(n);
  }
  public void run(){
    while(true){
      work();
    }
  }
}

public class MultiThreadProgram2{
  public MultiThreadProgram2(int jobcount){
    for(int i=0;i<jobcount;i++){
      new Thread(new RunnableJob(i)).start();
    }
  }
  public static void main(String[] args){
    new MultiThreadProgram2(10);
  }
}


○実行結果(マルチスレッド2)

……
[Job 9] is working.
[Job 8] is working.
[Job 4] is working.
[Job 9] is working.
[Job 9] is working.
[Job 5] is working.
[Job 6] is working.
[Job 8] is working.
[Job 2] is working.
[Job 7] is working.
[Job 9] is working.
[Job 3] is working.
[Job 0] is working.
……

○解説

プログラムは理解できたと思います。
シングルスレッドもマルチスレッドもやっていることは同じですね。
それでは、実行速度はどうでしょう?
マルチスレッドって本当に速いの!?

実際に実行してみると分かりますが、
マルチスレッドの方が圧倒的に速いです。

何故でしょう?
待ち時間の合計は同じはずでは?
これには、一つの可能性が考えられます。
すなわち、sleep メソッドで一時停止する時間はスレッドが切り替わってもカウントされ続ける。
もし仮に待ち時間が無いのであれば、
シングルスレッドとマルチスレッドの実行速度は同じということになります。

例題16-3-2 例題16-4 GoodBank クラスのフィールド、メソッドを static にして全体を書き換えて下さい。

○プログラム

//OneBank.java

public class OneBank{
  private static int value=0;
  public static synchronized void addMoney(int money){
    int currentValue=value;
    System.out.println(Thread.currentThread()+" が addMoney に入りました。");
    value+=money;
    if(currentValue+money!=value){
      System.out.println(Thread.currentThread()+" で矛盾が発生しました!");
      System.exit(-1);
    }
    System.out.println(Thread.currentThread()+" が addMoney から出ました。");
  }
}


//OneBankTest.java

public class OneBankTest extends Thread{
  public void run(){
    while(true){
      OneBank.addMoney(100);
      OneBank.addMoney(-100);
    }
  }
  public static void main(String[] args){
    new OneBankTest().start();
    new OneBankTest().start();
  }
}


○実行結果

……
Thread[Thread-0,5,main] が addMoney に入りました。
Thread[Thread-0,5,main] が addMoney から出ました。
Thread[Thread-1,5,main] が addMoney に入りました。
Thread[Thread-1,5,main] が addMoney から出ました。
……

○解説

OneBank クラスのインスタンスフィールド value をクラスフィールドに変更し、
インスタンスメソッド addMoney をクラスメソッドに変更したことで、
OneBankTest クラスにもいくらかの変更が加えられましたが、
その殆どは OneBank クラスのインスタンスを廃したことでしょう。

クラスフィールドはクラスにつき一つしか作られないフィールド。
synchronized クラスメソッドは呼び出すのにインスタンスを必要とせず、
クラスにつき一つのスレッドしか同時に実行することが出来ないメソッド、ですね。

プログラムでは同じクラスのスレッドを二つ作成していますが、
synchronized クラスメソッドを実行できるのは一方のみです。
例えるならば「二人のお客に対し、窓口は一つしかない」のと同じです。

それに対し、例題16-4のプログラムの場合は
「窓口は幾つもあるのに、二人のお客が一つの窓口に並んでいる」といったところでしょうか。

スレッドを強制的に切り替えるには?

いつスレッドの切り替えが起こるかは予測不能です。
しかし、任意のタイミングでスレッドの切り替えを起こすことは可能です。
もちろん、ここでしかスレッドの切り替えが起こらない、という意味ではありませんよ。

public static void yield()  //Thread クラス ← java.lang パッケージ
 現在実行中のスレッドオブジェクトを一時的に休止させ、ほかのスレッドが実行できるようにします。


戻る / ホーム