モノリシックモードの記録

4838 ワード

1つ目(怠け者、スレッドが安全ではありません):
public class Singleton {  
   private static Singleton instance;  
   private Singleton (){}  
 
   public static Singleton getInstance() {  
   if (instance == null) {  
       instance = new Singleton();  
   }  
   return instance;  
   }  
}  

この書き方lazy loadingは明らかだが,致命的なのはマルチスレッドで正常に動作しないことである.2つ目(怠け者、スレッドセキュリティ):
public class Singleton {  
   private static Singleton instance;  
   private Singleton (){}  
   public static synchronized Singleton getInstance() {  
   if (instance == null) {  
       instance = new Singleton();  
   }  
   return instance;  
   }  
}  

この書き方はマルチスレッドでよく機能し、lazy loadingも備えているように見えますが、残念ながら効率が低く、99%の場合同期は必要ありません.3つ目(餓漢):
public class Singleton {  
   private static Singleton instance = new Singleton();  
   private Singleton (){}  
   public static Singleton getInstance() {  
   return instance;  
   }  
}  


この方式はclassloderメカニズムに基づいてマルチスレッドの同期問題を回避するが,instanceはクラスマウント時にインスタンス化され,クラスマウントの原因は様々であるが,単例モードではgetInstanceメソッドを呼び出すことが多いが,他の方式(または他の静的メソッド)によるクラスマウントが特定できない.このときinstanceの初期化はlazy loadingの効果に達していないことは明らかである.第四種(餓漢、変種):
public class Singleton {  
   private Singleton instance = null;  
   static {  
   instance = new Singleton();  
   }  
   private Singleton (){}  
   public static Singleton getInstance() {  
   return this.instance;  
   }  
}  

表面的には差が大きいように見えますが、実は3つ目の方法は差が少なく、クラス初期化すなわちインスタンス化instanceです.5つ目(静的内部クラス):
public class Singleton {  
   private static class SingletonHolder {  
   private static final Singleton INSTANCE = new Singleton();  
   }  
   private Singleton (){}  
   public static final Singleton getInstance() {  
   return SingletonHolder.INSTANCE;  
   }  
}  

この方式は同様にclassloderのメカニズムを利用してinstanceを初期化する際に1つのスレッドしかないことを保証し、3つ目と4つ目の方式とは異なり(細かい違い):3つ目と4つ目の方式はSingletonクラスがマウントされるとinstanceがインスタンス化され(lazy loading効果に達しない)、この方式はSingletonクラスがマウントされ、instanceは必ずしも初期化されるとは限らない.SingletonHolderクラスはアクティブに使用されていないため、getInstanceメソッドを呼び出すとSingletonHolderクラスのマウントが表示され、instanceがインスタンス化されます.想像してみてください.インスタンス化instanceがリソースを消費している場合、私は彼にロードを遅らせたいと思っています.一方、Singletonクラスのロード時にインスタンス化したくないです.Singletonクラスが他の場所でアクティブに使用されてロードされる可能性があることを確保できないので、この時にインスタンス化instanceは明らかに適切ではありません.この時、この方式は3番目と4番目の方式に比べて合理的に見えます.6つ目(列挙):
public enum Singleton {  
   INSTANCE;  
   public void whateverMethod() {  
   }  
}  

この方式はEffective Javaの著者Josh Blochが提唱している方式であり,マルチスレッド同期の問題を回避できるだけでなく,逆シーケンス化による新たなオブジェクトの再作成を防ぐことができ,強固な障壁と言えるだろうが,個人的には1.5にenum特性が加わったため,この方式で書くと疎遠に感じられ,実際の作業では,私もそう書いた人をめったに見ません.第七種類(二重チェック錠):
public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}  

これは第2の方法のアップグレード版で、俗に二重チェックロックと呼ばれています.詳しくは以下を参照してください.http://www.ibm.com/developerworks/cn/java/j-dcl.htmlJDK 1.5以降、二重検査ロックが正常に単例効果を達成することができる.
まとめるには2つの問題に注意しなければならない.単一のインスタンスが異なるクラス・マウンタによってロードされると、複数の単一のクラスのインスタンスが存在する可能性があります.リモートアクセスではないと仮定します.たとえば、いくつかのservletコンテナでは、各servletに対して全く異なるクラスマウンタが使用されます.これにより、2つのservletが1つの単一のクラスにアクセスすると、それぞれのインスタンスがあります.2.Singletonがjavaを実現する場合.io.Serializableインタフェースでは、このクラスのインスタンスがシーケンス化され、復元される可能性があります.いずれにしても、単一クラスのオブジェクトをシーケンス化し、次に複数のオブジェクトを復元すると、複数の単一クラスのインスタンスが作成されます.最初の問題を修正する方法は、次のとおりです.
private static Class getClass(String classname)      
                                         throws ClassNotFoundException {     
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();     
      
      if(classLoader == null)     
         classLoader = Singleton.class.getClassLoader();     
      
      return (classLoader.loadClass(classname));     
   }     
}  

2つ目の問題を修正する方法は、次のとおりです.
public class Singleton implements java.io.Serializable {     
  public static Singleton INSTANCE = new Singleton();     
     
  protected Singleton() {     
       
  }     
  private Object readResolve() {     
           return INSTANCE;     
     }    
}   

私にとって、私は3つ目と5つ目の方法が好きで、分かりやすくて、しかもJVM層でスレッドセキュリティを実現しました(複数のクラスのローダ環境でなければ)、一般的には3つ目の方法を使います.lazy loading効果を明確に実現する時だけ5つ目の方法を使います.また、逆シーケンス化に関連するオブジェクトの作成については、列挙を使用して単一の例を実装してみますが、私はずっと私のプログラムがスレッドの安全であることを保証しています.そして、私は永遠に第1の方法と第2の方法を使用しません.もし他の特別な需要があれば、私は第7の方法を使用するかもしれません.結局、JDK 1.5二重チェックロックの問題はありません.
しかし、一般的には、1つ目は単例ではなく、4つ目と3つ目が1つで、計算すれば5つ目も別々に書くことができます.だから、一般的に単例は5つの書き方です.怠け者、悪漢、二重チェックロック、列挙と静的内部クラス.転載は出典を明記してください.http://cantellow.iteye.com/blog/838473