java単一例-ブロックシリーズ

14152 ワード

一歩ずつ知識をまとめてみましょう。昔は分かるようで分からないようで、表面を知っていて、知らないことを知っています。
 
怠け者タイプの一例:
私有化コンストラクタは、外部のインスタンスオブジェクトをブロックし、getInstance静的方法を呼び出して、すでに実用化されているかどうかを判断します。
なぜ怠け者なのかというと、これは遅延負荷の例に属しています。つまり、来なくても対象を非現実化しています。
public class Singleton {
   private static Singleton instance;
   private Singleton (){}

   public static Singleton getInstance() {
      if (instance == null) {
         instance = new Singleton();
      }
      return instance;
   }

}
スレッドの安全問題:マルチスレッドが同時にget Instance()を起動すると同時にinstanceを判断し、実用化された操作に入ると、単利は存在しない。
スレッドの安全のために、get Instance()方法を同期化します。
public class Singleton {
  private static Singleton instance;
  private Singleton (){}

  public static synchronized Singleton getInstance() {
    if (instance == null) {
        instance = new Singleton();
    }
    return instance;
  }
}
synchronized修飾は、同じ時間に一つのスレッドだけがgetInstance方法を実行することができ、このようにして、一例のスレッドの安全を保証することができる。しかし、同期粒度が大きすぎるようです。実際には、インスタンス初期化後、スレッドの整列を保証する必要はありません。
二重検査錠のコードを引き出します。
public class Singleton {
  private static Singleton instance;
  private Singleton (){}

  public static Singleton getSingleton() {
      if (instance == null) {                        //Single Checked
          synchronized (this) {
              if (instance == null) {                //Double Checked
                  instance = new Singleton();
              }
          }
      }
      return instance ;
  }
}
同期が速い外部と内部のチェックを2回行い、同期ブロックに入るスレッドに新しいインスタンスが発生しないことを保証する。
複数のスレッドが同時に進行している場合、例はnullであると判定されると、競合ロックが開始され、一方のスレッドがロック解除されて実装され、他のスレッドは待つ必要があり、実装されたスレッドが解放された後、ログインしたスレッドが単一のインスタンスが発生しているかどうかを判断しないと、またオブジェクトを実装することができなくなる。
このようなダブルチェックロックは完璧に開かれていますが、コマンドの並び替えは問題を引き起こします。これも勉強の並び替えのいい例だと思います。
instance = new Singleton();
上のコードは原子操作ではなく、命令に訳せません。
これは次の三つの命令によって完成されます。
  instance     
   Singleton              
 instance             
JVMコンパイル時にコマンドの並べ替えを行うと、上の3つの命令の実行順序が狂ってしまう可能性があります。つまり、先に1、3をまっすぐ行ってから2を実行します。1と3を実行した場合、instanceはnullではなく、新しく入ったスレッドは最初のnullを判断した時に、直接に2ステップを実行していない例を返します。これは確かに経典的なシーンです。
その他の読書
例を初期化したら、第三段階を別々に書いて、この問題を解決できそうです。コードは以下の通りです。
public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                Singleton temp = instance;
                if (temp == null) {
                    synchronized (Singleton.class) {
                        temp = new Singleton();
                    }
                    instance = temp;
                }
            }
        }
        return instance;
    }
volatileキーワードは、実際にコンパイル時の並べ替えに対して障壁を設けています。具体的な諸説は次の文章を読むことができます。
 拡大して読む
上から感じることができて、積み重ねる時初期化して例をよくして、多くの考慮するべきなものがあることができて、もしコンパイルの段階で実際化するならば、このように合併の持ってくる問題を免れることができます。
それはいわゆる餓漢式の一例です。
public class Singleton{

    //        
    private static final Singleton instance = new Singleton();

    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
}
もちろんこのようにして自然に自分の欠点があります。この一例は使われていない時にすでに実用化が必要です。そうするとメモリを無駄に使ってしまいます。
あるいは、このような方法の実装では、外部パラメータに依存した例示的なシーンは実現できなくなります。
もう一つのオススメの書き方があります。
public class Singleton {
  private static class SingletonHolder {
    private static final Singleton INSTANCE = new Singleton();
  }
  private Singleton (){}
  public static final Singleton getInstance() {
    return SingletonHolder.INSTANCE;
  }
}
もう一つの大家さんのオススメの書き方がありますが、大柄なところがありますか?
public enum EasySingleton{

    INSTANCE;
}
 
どのように単一の例を破壊するかを見てみましょう。
1,プロローグと反プロローグ
もちろん前に書いたコードは序列化と反序列化が必要ではありません。この問題はありません。ただ、これを送る側はどのようにそれを破壊するかを考えています。以下の例を参照してください。
public class Singleton implements Serializable{
    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;
    }
}
public class SerializableDemo1 {
    //      ,              。           
    //Exception    
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //Write Obj to file
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
        oos.writeObject(Singleton.getSingleton());
        //Read Obj from file
        File file = new File("tempFile");
        ObjectInputStream ois =  new ObjectInputStream(new FileInputStream(file));
        Singleton newInstance = (Singleton) ois.readObject();
        //          
        System.out.println(newInstance == Singleton.getSingleton());
    }
}
//false
 
2,反射
public class Singleton {
    public static final Singleton INSTANCE = new Singleton();
    private Singleton() {
    }
    public Singleton getInstance() {
        return INSTANCE;
    }
    public static void main(String[] args) throws Exception {
        //           
        Class clazz = Singleton.class;
        Constructor c = clazz.getDeclaredConstructor();
        //       private       !!!
        c.setAccessible(true);
        //                   
        System.out.println(Singleton.INSTANCE == c.newInstance());
    }
}
単一の例を破壊する原理は,構造関数を使わずにインスタンスを生成することができます。私たちは構造関数をオフにしただけです。
 
これまでjavaの単一の例に対して、より全面的な認識があり、多くの知識点に関連して、引き続き掘り起こす必要があります。
 
 
 
歩き続けましょう。
----------------------------------------
努力が成功するとは限らないが、努力しないことには必ず成功しない。