多重スレッド

  1. 多重スレッド

    1. スレッドクラスの継承

       スレッドはスレッドを生成した「本体」とは独立に実行されるプログラムの仕組みでした。アプレットにスレッド機能を付加するには、 
       public class digwatch extends Applet implements Runnable
      のように、Runnable を組み込み(implements)ました。Runnableはインターフェースと呼ばれるもので、extends よりは少し制限された機能です。しかし、Javaでは継承できるのは一つのクラスに限定されているため、インターフェースの組み込みで代用したのです。

    2. Threadクラス

       アプレットクラスとは別のクラスを定義し、それに、Threadクラスを extends(継承)すると、そのクラスを スレッド として、アプレットとは別に並列動作させることができます。たとえば、以下のようにThread を extends した Ball クラスを作成します。
       class Ball extends Thread { }
      このクラスのインスタンス(実体)をnewで作成し、start() メソッドを呼び出すことで、ball1は独立したスレッドとして実行されます。
       ball1=new Ball( );
       ball1.start();
      同じ処理を、もう一つ同時に実行するには、
       ball2=new Ball( );
       ball2.start();
      とすればよいことになります。

  2. ballクラスとコンストラクタ

    1. コンストラクタ

      クラスの名前と同じ名前の特殊(戻りの型がない)メソッドはコンストラクタと呼ばれ、このクラスを new で作成するとき同時に呼び出されます。この例では、int dx,int dy,int w, int h を引数として受け取り、ボールの移動量、移動範囲を受け取ります。
       this.w = w;
      の this は、Ballクラス自身のことで、引数の w と自分で(先頭に)定義したフィールド変数のwを区別しています。

    2. runメソッド

       runメソッドは、sleep(30); で30m秒休止しながら move メソッドを呼び出しボールを移動します。このメソッドが並列処理の対象になります。

    3. move()

       ボールに位置は x,y で記録します。一回呼び出される度にこの値を、dx,dy だけ、加えます。ただし、x<0,y<0 になったり、幅w、高さhの領域を越えると、dx,dy の符号を変えて、「跳ね返り」を行います。
      例 if (x <= 0) { x = 0; dx = -dx; }

    4. draw(Graphics g)

       このメソッドは、アプレットの draw() から呼び出されます。自分のボールをアプレットから引き渡される Graphics g に表示します。

    5. tstop()

       スレッドの停止用のメソッドで、run() の繰り返しを止めます。これは、アプレットがBallクラスのスレッドを停止するとき使用します。
          public void tstop() {
              cont = false;
          }

    6. ソース


      import java.awt.*;
      public class Ball extends Thread {
      
          int w = 15;
          int h = 15;
          int x = 0;
          int y = 0;
          int dx = 2;
          int dy = 2;
          boolean cont = true;
      
          public Ball(int dx, int dy, int w, int h) {
              this.w = w;
              this.h = h;
              this.dx = dx;
              this.dy = dy;
          }
      
          public void run() {
              while (cont) {
                  move();
                  try {
                      sleep(30);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }
      
          void draw(Graphics g) {
              //System.out.println(&quot;draw ball&quot;);
              g.fillOval(x, y, 5, 5);
          }
      
          public void move() {
              x += dx;
              y += dy;
              if (x <= 0) {
                  x = 0;
                  dx = -dx;
              }
              if (x >= w) {
                  x=w;
                  dx = -dx;
              }
              if (y <= 0) {
                  y = 0;
                  dy = -dy;
              }
              if (y >= h) {
                  y = h;
                  dy = -dy;
              }
          }
      
          public void tstop() {
              cont = false;
          }
      }
      ここにソースがあります。
  3. 多重スレッドの実行

    1. アプレットクラス

      アプレットクラス(MultiThread)では、Ball クラスのインスタンスを3個生成し、各スレッドを起動します。各ボールを表示するには、ボールクラスの draw() メソッドを利用しますが、連続して表示するためには、アプレットにもスレッドを組み込みます。

    2. init():初期化

       init() はアプレットが起動されるとき、最初に呼び出されるメソッドですね。ここでは、まず、アプレットの画面のサイズを w,h に取り出します。次に、ball1 = new Ball(1, 2, w, h); で、Ballのクラスのインスタンスball1を作成し、このスレッドの実行を開始します。
      w = this.getWidth();
      h = this.getHeight();
      ball1 = new Ball(1, 2, w, h);
      ball1.start();
      同様に、ball2,ball3 のスレッドも起動し、最後に、
       drawBall = new Thread(this);
       drawBall.start();
      で、自分自身のスレッドの実行を開始します。

    3. run

       スレッドの実行部分は、50m秒休みながら、repaint() で再表示をするだけです。contは boolean 型で、これをfalseにすると、スレッドの実行を停止できます。

    4. paint

       ここでは、各ボールの表示を行いますが、実際の表示は各Ballスレッドの中の draw() メソッドに頼んでいます。
          public void paint(Graphics g) {
              ball1.draw(g);
              ball2.draw(g);
              ball3.draw(g);
          }

    5. stop()

       アプレットで stop() メソッドを定義すると、アプレットが終了するとき、実行をしてくれます。ここでは、スレッドを実行していますが、これは、アプレットが実行を停止しても、すぐには停止しません。自分で起動したものは、停止してから終了するのが礼儀(?)でしょう。
       アプレットのスレッドは cont をfalse にすると停止します。他のスレッドの停止は、Ballクラス側で用意した tstop() メソッドを利用します。

    6. ソース

      3個のボールをパラメータを変えて作成し、表示します。移動処理は、各ボールのスレッドで実行します。
      import java.applet.Applet;
      import java.awt.*;
      
      //<APPLET CODE="MultiThread.class" WIDTH=250 HEIGHT=250></APPLET>
      
      public class MultiThread extends Applet implements Runnable {
      
          Ball ball1, ball2, ball3;
          int w, h;
          Thread drawBall;
          boolean cont = true;
      
          public void init() {
              w = this.getWidth();
              h = this.getHeight();
      
              ball1 = new Ball(1, 2, w, h);
              ball1.start();
              ball2 = new Ball(2, 1, w, h);
              ball2.start();
              ball3 = new Ball(3, 2, w, h);
              ball3.start();
              drawBall = new Thread(this);
              drawBall.start();
          }
      
          public void run() {
              while (cont) {
                  try {
                      Thread.sleep(50);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  repaint();
              }
          }
      
          public void paint(Graphics g) {
              ball1.draw(g);
              ball2.draw(g);
              ball3.draw(g);
          }
      
          public void stop() {
              cont = false;
              ball1.tstop();
              ball2.tstop();
              ball3.tstop();
          }
      }
      ここにソースがあります。このクラスを実行するには Ball クラスを必要です。Jcpad上で このソースだけでなく、ファイルメニューから新規作成し、Ball.java のソースも組み込み、同じフォルダに保存します。MultiThread.java をコンパイルすると、Ball.java も同時にコンパイルされます。

    7. ボールのアニメーションの流れ

       ここで、もう一度、全体の流れを見直しましょう。まず、アプレットの init() で、3個の Ball クラスのインスタンスが作成され、実行が開始されます。これで、3個のスレッドの動作が開始され、run() メソッドで各スレッドの mov() が実行されます。mov() の処理は同じですが、使用している dx,dy の値が異なるため、各ボールの移動方向は異なります。
       このボールを表示をしているのが、アプレット側のスレッド drawBall です。これは、50m秒単位で、アプレットのpaint()を呼び出し、paint()は各Ballスレッドの draw() を呼び出してボールの表示を依頼します。この時、Graphics を引き数で渡しています。
       draw()で表示する位置はmov()メソッドで変更されますから、アプレットのpaint()で表示されるボールの位置も変化します。

    8. 実行

      これは、ボタンなどありませんから、起動すれば動き出します。



  4. スカッシュゲーム


    1. スカッシュゲーム

      ボールが動くだけでも面白くないので、跳ね返って落ちてくるボールをマウスで操作するパッドで跳ね返すミニゲームを作成してみましょう。
       落ちてくるボールをパッドで受け止めます。ボールとパッドとの「衝突判定」はちょっと面倒です。パッドにボールのサイズを足した範囲にボールが落ちてくれば、「衝突」と判定し、ボールをはね返します。このとき、同じ方向に跳ね返すと、同じ軌道を繰り返す場合がありますから、乱数でx方向のボールの速度を変更します。
       こちらのソースを参考にしてください

  5. 演習、アンケート

    1. 変形するボール

       形や色が変化するボールを複数飛ばしてください。
      ヒント:Ball() の引数を追加します。

    2. 「スカッシュ」

       スカッシュゲームを実行して、動作を確認してください。また、プログラムを変更して複数のボールにするなど、動きに変化をつけてください。

    3. アンケート

      スレッドの考え方は理解できましたか?
        1.できた  2.たぶん  3.できない

      プログラムは実行できましたか?
       1.できた  2.たぶん  3.できない

      演習はできましたか?
       1.できた  2.だいたい  3.できない