JAVA単例モードとマルチスレッド


単例モード単例モードは一般的な設計モードであり、
単例モードは3種類に分けられる:怠け者式単例、餓漢式単例、内部類単例、登録式単例のいくつか.
単一のパターンには次のような特徴があります.
1、単一のクラスには1つのインスタンスしかありません.
2、単一のインスタンスクラスは自分で自分の唯一のインスタンスを作成しなければならない.
3、単一のクラスは、他のすべてのオブジェクトにこのインスタンスを提供する必要があります.
怠け者モードはスレッドが安全ではありません.
餓漢モードはスレッドが安全です.
内部内モードはClassloaderの特
レジストリ・モードは、スレッドが安全であると同時に、実行期間指定の一例を提供します.
餓漢式単例類

public class Singleton    
{    
    private Singleton(){    
        
     }    
   
    private static Singleton instance = new Singleton();    
   
    private static Singleton getInstance(){    
        return instance;    
     }    
}   

内部クラス単例クラス
public class Singleton      
{         
        private Singleton(){      
          
     }      
     
    private class SingletonHoledr(){      
        private static Singleton instance = new Singleton();      
     }      
     
    private static Singleton getInstance(){      
        return SingletonHoledr.instance;      
     }      
}   
怠け者式単例類
同期は可能ですが、効率は高くありません.
Javaコード
public class Singleton       
{          
    private Singleton(){       
      
     }       
      
    private static Singleton instance;       
    public static synchronized Singleton getInstance(){       
        if(instance == null){       
            return instance = new Singleton();       
         }else{       
            return instance;       
         }       
     }       
}     
 

このようにプログラムを書くのは間違いありません.getInstance全体が全体の「critical section」なので、効率が悪いです.私たちの目的は、instanceを最初に初期化するときにlocking(ロック)が必要で、後でinstanceを取るときにスレッド同期を必要としないからです.
そこで賢い人々は次のような方法を考え出しました.
ダブルロックの書き方:
Javaコード
public class Singleton{    
  private static Singleton single;    //                
  private Singleton(){}    //          
      
  public static Singleton getSingle(){    //                   
    if(single == null){       
        synchronized (Singleton.class) {   //                                
            if(single == null){        
                 single = new Singleton();            
         }       
       }    
     }      
    return single;   //            
   }    
}  
public class Singleton{
   private static Singleton single;     //            
   private Singleton(){}     //       
   
   public static Singleton getSingle(){     //               
     if(single == null){    
          synchronized (Singleton.class) {    //                             
              if(single == null){     
                  single = new Singleton();          
         }    
       }
     }  
     return single;    //         
   }
}
の考え方は簡単です.つまり、instanceのコードを同期(synchronize)して初期化するだけで、コードが正確で効率的になります.
これがいわゆる「ダブルロック」メカニズム(その名の通り).
残念ながら、このような書き方は多くのプラットフォームや最適化コンパイラで間違っています.
なぜなら、instance=new Singleton()という行のコードの異なるコンパイラでの動作は予知できないからです.最適化コンパイラは、instance=new Singleton()を合法的に以下のように実現することができます.
1.instance=新しいエンティティにメモリを割り当てる
2.Singletonのコンストラクション関数を呼び出してinstanceのメンバー変数を初期化
スレッドAとBがgetInstanceを呼び出し、スレッドAが先に入り、ステップ1まで実行するとcpuが蹴られたことを想像してみてください.そしてスレッドBが入り、Bが見たのはinstanceがnullではありません(メモリが割り当てられています)すると、安心してinstanceを使用し始めましたが、これは間違っています.この時点で、instanceのメンバー変数はデフォルト値であり、Aはまだステップ2を実行してinstanceの初期化を完了する時間がありません.
もちろん、コンパイラは次のように実現できます.
1.temp=メモリの割り当て
2.tempのコンストラクション関数を呼び出す
3. instance = temp
もしコンパイラの行為がそうであれば、私たちは問題ないようですが、事実はそんなに簡単ではありません.あるコンパイラが具体的にどのようにしたのか分からないからです.Javaのmemory modelではこの問題を定義していないからです.
デュアルチェックロックはintなどのベース・タイプに適用されます.ベース・タイプがコンストラクション関数を呼び出すステップがないため、明らかでしょう.
http://hi.baidu.com/rtsbtx/blog/item/80c9693b306e1ee114cecb5e.html