共有変数と同期



複数のスレッドが同じデータを操作する時は,予測の付かない動きをするこ ともある.次のプログラムは,10個のスレッドが,圧力計の値を見て,安全な ら圧力を加えるというプログラムである.
// pressureを Threadのサブクラスとして定義
// Threadによって動く実体とする
class pressure extends Thread{
  // 圧力が安全圏内なら加える
  static void RaisePressure(){
    // 現在の圧力が制限値よりも15を超して少ないなら
    int pval=p.pressureGauge;
    if(pval < p.safetyLimit-15){
      // わざと 100ms(0.1秒)待つ
      try{sleep(100);} catch(Exception e){}
      // 圧力を15加える
      p.pressureGauge+=15;
      System.out.println("pval="+pval+", p.pressureGauge="+p.pressureGauge);
    }
    else{
      System.out.println("Failed adding pressure");
    }
  }
  // pressureのインスタンスに対して startするとここが呼ばれる.
  public void run(){
    RaisePressure();
  }
}

public class p {
  // pクラスの静的変数なのでプログラム全体から共有される
  // 圧力値
  static int pressureGauge=0;
  // 圧力の制限値
  static final int safetyLimit=20;
  
  public static void main(String[] args){
    pressure [] p1=new pressure[10];
    int i;
    // 10個の Threadを作り,次々と作成
    for(i=0;i<10;i++){
      p1[i]=new pressure();
      p1[i].start();
    }
    // すべてのスレッドが終了するのを待つ
    try{
      for(i=0;i<10;i++) p1[i].join();
    }
    catch (Exception e){}
    System.out.println("gauge reads "+pressureGauge+", safe limit is 20");
  }
}
これを実行すると,
pval=0, p.pressureGauge=15
pval=0, p.pressureGauge=30
pval=0, p.pressureGauge=45
pval=0, p.pressureGauge=60
pval=0, p.pressureGauge=75
pval=0, p.pressureGauge=90
pval=0, p.pressureGauge=105
pval=0, p.pressureGauge=120
pval=0, p.pressureGauge=135
pval=0, p.pressureGauge=150
gauge reads 150, safe limit is 20
となり,範囲を超えてしまうだろう.これを防ぐために,Java言語では相互排 他(mutual exclusion)を実現するためのsynchronized というキーワードを用 意してある.この使い方は,メソッドにつける.ブロックにつけるなどいろい ろある.先ほどの例では,
// pressure1を Threadのサブクラスとして定義
// Threadによって動く実体とする
class pressure1 extends Thread{
  // 圧力が安全圏内なら加える
  static synchronized void RaisePressure(){
    // 現在の圧力が制限値よりも15を超して少ないなら
    int pval=p1.pressureGauge;
    if(pval < p1.safetyLimit-15){
      // わざと 100ms(0.1秒)待つ
      try{sleep(100);} catch(Exception e){}
      // 圧力を15加える
      p1.pressureGauge+=15;
      System.out.println("pval="+pval+", p1.pressureGauge="+p1.pressureGauge);
    }
    else{
      System.out.println("Failed adding pressure");
    }
  }
  // pressureのインスタンスに対して startするとここが呼ばれる.
  public void run(){
    RaisePressure();
  }
}

public class p1 {
  // p1クラスの静的変数なのでプログラム全体から共有される
  // 圧力値
  static int pressureGauge=0;
  // 圧力の制限値
  static final int safetyLimit=20;
  
  public static void main(String[] args){
    pressure1 [] p1=new pressure1[10];
    int i;
    // 10個の Threadを作り,次々と作成
    for(i=0;i<10;i++){
      p1[i]=new pressure1();
      p1[i].start();
    }
    // すべてのスレッドが終了するのを待つ
    try{
      for(i=0;i<10;i++) p1[i].join();
    }
    catch (Exception e){}
    System.out.println("gauge reads "+pressureGauge+", safe limit is 20");
  }
}
のようにRaisePressureにsynchronized というキーワードを付けると,同じク ラスのオブジェクトが,RaisePressureを同時に実行するのを防ぐことができ る.
メソッドRaisePressureが static になっていないと,synchronizedにより, 「同時には実行しない」単位が,「同じインスタンスに対するメソッド RaisePressureの呼び出し」というものになるので,このケースではふさわしくない.

アニメーション