[an error occurred while processing this directive] [an error occurred while processing this directive]

Javaでマルチスレッド

まずはサンプル

まずマルチスレッドのサンプルですが、
[ThTest.java]
class Runner implements Runnable{
  public void run() { 
    for(int i = 0; i < 3; i++) System.out.println("count :" + i);
  }
}

public class ThTest { 
  public static void main(String[] args){
    Runner runner = new Runner();
    Thread t = new Thread(runner);
    t.start();
  }
}
ま、こんな感じかな。 Runnerのrun()が実際に走るスレッドになります。 mainの方はまとめると
  public static void main(String[] args){
    (new Thread(new Runner())).start();
  }
という荒っぽいこともできます。

Synchronizedの話題

Javaのオブジェクトの多くは、そのまま書いただけでは マルチスレッドで走らせると、複数のメソッドが同時に動いたりしてバグります。 だから、synchronizedを指定して「ここにアクセスするのは同時に一つだけ」と 指定します。
Vectorなど元々スレッドセーフなクラスを使うときは、 一つのメソッドが同時に呼ばれても問題はないようになってい(ると思い)ますが、 ArrayListなどスレッドセーフになっていないものは明示的に指定する必要があります。 さらに、プログラムの要請からsynchronizedを使って独自の条件を指定したりまできます。
例えば、ここの結晶のアプレットでは 結晶の座標を計算するスレッドとアニメーションを描画するスレッドがあり、 計算スレッドが計算した結果を待ち行列(キュー)に入れていき、 アニメーションスレッドはそこから取り出す、という作業をしています。 ここで、キューは同時に挿入と削除が行われる可能性があり、しかも 削除の方はデータが空のときは計算されるまで待たせる必要があります。
この条件を満たすクラスとして、以下のものを書いてみました。
[MyQueue.java]
import java.util.List;
import java.util.ArrayList;

public class MyQueue{
  private List q = new ArrayList();

  public void enqueue(Object o){
    synchronized(q){
      q.add(o); 
      q.notify();
    }
  }

  public Object dequeue() {
    synchronized(q){
      while(q.isEmpty())
      try{ q.wait();} catch (InterruptedExceprion e){}
      return q.remove(0);
    }
  }
}
synchronized(q)というのは、「このブロック内ではqに同時に複数アクセスできない」 ということを指定します。 ここで、複数の同じオブジェクトを対象とするsynchronizedでも、それぞれは連携して 動作し、同時に一つのブロックしか動きません。 例えばenqueue()とdequeue()が同時に呼ばれても、実際にqに触ることが出来るのは 片方だけで、もう片方はその直前でブロックされます。
しかし、この機能だけだと、dequeueの方で「キューが空の間は、データが入るまで待つ」 という処理が書けないので、wait()とnotify()という仕組みが用意されています。 synchronizedブロックの中でwait(100)という風に呼ぶと、最小100m秒間だけ、そのsynchronized ブロックのロックが解除されます。そこで、
 while(q.isEmpty()) q.wait(100);
という風に書くと、100m秒おきにキューが空でないかチェックし、空でなくなったら 次の処理に移ることができるようになっています。
また、ここで
 while(q.isEmpty()) q.wait();
というふうに書くと、スレッドは永久にブロックされます。 って、本当に永久にブロックされたら困るわけで、このブロックを起こす手段がちゃんと用意されています。 それがq.notify()というメソッドで、どこか別のブロックでこのnotifyが呼ばれると、 waitは解除されます。ここでは、enqueueの方でnitify()してるので、 dequeueのwaitはキューが空でなくなったら解除されます。
一応、このキューのテストプログラム。
[Qtest.java]
import dla.MyQueue;

class Producer implements Runnable{
  MyQueue q;
  Producer(MyQueue _q){q = _q;}
  public void run() {
    for(int i = 0; i < 3; i++){
      q.enqueue("Nexus" + i);
      try{
        Thread.sleep(1000); 
      } catch (Exception e){}
    }
  }
}

class Consumer implements Runnable{
  MyQueue q;
  public Consumer(MyQueue _q){q = _q;}
  public void run() {
    for(int i = 0; i < 3; i++){
      try{
        System.out.println((String)q.dequeue());
        Thread.sleep(1);
      } catch (Exception e){}
    }
  }
}

public class Qtest { 
  public static void main(String[] args){
    MyQueue q = new MyQueue();
    (new Thread(new Producer(q))).start();
    (new Thread(new Consumer(q))).start();
  }
}
データを入れるほうは1000msecおきにenqueueし、取り出すほうは1msecおきにdequeueするから、 waitが何度も呼ばれている計算になります。
[an error occurred while processing this directive]