マルチスレッドプログラミングの設計モード


臨界領域モードCritical Section Patternとは、1つの共有範囲において1つのスレッドのみを実行するモードである.
他のすべてのマルチスレッド設計モードの基礎であるため、まず紹介します.
着目点を範囲に置く、このモードを臨界領域モードといい、実行するスレッドに着目点を置くと、このモードは
シングルスレッド実行モード
まず洞窟を掘るゲームをしましょう.私のAxman、友达のSager、同僚Pentium 4.3人で八角遊園地に
循环钻山洞(KAO,减肥训练啊),每个人手里有一个牌子,每一钻洞口的老人会把现在的顺序,
名前、ナンバープレートが表示され、名前とナンバープレートが一致しているかどうかを確認します.
はい、このゲームの参加者は遊園地のおじいさんGeezer、Player、私たちです.洞窟corrieもあります.

   public class Geezer {
      public static void main(String[] args){
         System.out.println("  ,  !"); 
         Corrie c = new Corrie(); //      ,             Player.
         new Player("Axman","001",c).start(); 
         new Player("Sager","002",c).start(); 
         new Player("Pentium4","003",c).start(); 
      }
   }

このクラスはしばらく何も言わないで、それはMainの役です.

    public class Player extends Thread{
        private final String name; 
        private final String number; 
        private final Corrie corrie; 
        public Player(String name,String number,Corrie corrie) {
            this.name = name; 
            this.number = number; 
            this.corrie = corrie; 
        }

        public void run(){
           while(true){
              this.corrie.into(this.name,this.number); 
           }
        }
    }

ここでは、メンバーフィールドをfinalに設定し、Playerが構築されると名前とナンバープレートを変更できないことを説明します.
簡単に言えば、ゲームの中で、私、Sager、Pentiumの4人は自分でこっそり自分のナンバープレートを変えることはできなくて、こっそり行くこともできません
別の洞窟を潜って、もしこのゲームがいったん間違いが発生したら、間違いは私たちのプレイヤーではありません.

   import java.util.*; 
   public class Corrie {
      private int count = 0; 
      private String name; 
      private String number; 
      private HashMap lib = new HashMap(); //         

       public Corrie(){
         lib.put("Axman","001"); 
         lib.put("Sager","002"); 
         lib.put("Pentium4","003"); 
      }

      public void into(String name,String number){
          this.count ++; 
          this.name = name; 
          this.number = number; 
          if(this.lib.get(name).equals(number))
           test():
      }

      public String display(){
        return this.count+": " + this.name + "(" + this.number + ")"; 
      }
      private void test(){
        if(this.lib.get(name).equals(number)); 
          System.out.println("OK:" + display()); 
        else
          System.out.println("ERR:" + display()); 
      }
   }

このクラスにはlibのHashMapが追加され、プレイヤーの名前とナンバープレートのライブラリに相当します.Corrieにはインスタンスが1つしかないことを知っているので、
だから私は静的インスタンスではなくメンバーオブジェクトを使用しました.ただ、構築方法でライブラリの内容を初期化するために、本当の意味では
このようなデータ構造のカプセル化機能を1つの補助クラスで実現する.このlibを提供しなければcheckの時に使います
if(name.equasl("Axman")){
if(!number.equals("001")/エラー
}
else if .......
このような複雑な文は、プレイヤーが手がけいれんする可能性が高いため、libを1つ使ってchcekを作るとよく似ています.
このプログラムを実行するには忍耐力が必要です.あなたのプログラムがどんなに悪くても、多くの単一スレッドテスト環境では正しいからです.
また、マルチスレッドプログラムは異なるマシンで異なることを示しています.この例の誤りを発見するには、長い間実行する必要があります.
機械はCPUが多いので、間違いが発生する機会はずっと大きいです.
私のノートに最終的にエラーが発生したのは11分後で、エラーは数時間後に発生しました.
1: ERR:Axman(003)
2: ERR:Sager(002)
1つ目はエラーが検出され、私のナンバープレートは001なのに003が印刷され、2つ目はエラーがないのにエラーが印刷された.
実際,以前に紹介したマルチスレッド知識によれば,この例の誤りが現れるのは理解に難くないが,intoはスレッドが安全ではないため,その中で
スレッドがthisを実行します.name = "Axman"; その後、本来はthisを実行すべきである.numner="001"なのに別のスレッドに切り替えて実行
this.number="003"は、その後、いずれかのif(this.lib.get(name)を予知できない切替で実行する.equals(number))
1のエラーが発生して、このエラーを印刷する時displayもスレッドが安全ではないため、1つのエラーの結果を印刷しようとする時、
this.名前かthis.numberのいずれかのフィールドが修正するが、正しく一致してエラーが発生する.
また、シーケンス番号が逆転したり対応していない可能性もありますが、このエラーは直感的に観察できません.どのシーケンス番号が「べき」なのか分からないからです.
どのPlayerを与えるか、シーケンス番号が逆になるとスクロールする画面に隠される可能性がある.
[正しいCritical Sectionモードの例]
これらのエラーは、Corrieクラスのメソッドがスレッドセキュリティではないため、Corrieクラスがスレッドセキュリティのクラスであることを変更すればよいことを知っています.
はい.他のクラスは修正する必要はありません.間違いが発生したら、私たちのプレイヤーのことではありません.

   import java.util.*; 
   public class Corrie {
       private int count = 0; 
       private String name; 
       private String number; 
       private HashMap lib = new HashMap(); //         

        public Corrie(){
          lib.put("Axman","001"); 
          lib.put("Sager","002"); 
          lib.put("Pentium4","003"); 
       }

      public synchronized void into(String name,String number){
          this.count ++; 
          this.name = name; 
          this.number = number; 
          test(); 
      }

      public synchronized String display(){
         return this.count+": " + this.name + "(" + this.number + ")"; 
      }

      private void test(){
        if(this.lib.get(name).equals(number)); 
        //System.out.println("OK:" + display()); 
        else
         System.out.println("ERR:" + display()); 
      }
  }

この例を実行して、もしあなたの忍耐力があれば、あなたの機械を運転して3日間実行しましょう.テスト100日目は101日目にエラーがなかったとは言えませんが、
at least,現在の正しさはsynchronized保護のない例よりずっと信頼できる!