Java単例モードの6種類の書き方

3111 ワード

Javaの単例モードの書き方はいろいろありますが、その書き方がどんなに変わっても、この3つのステップは欠かせません.
  • プライベート静的変数
  • 私有の構造方法
  • に共通する静的取得例の方法
  • 1、怠け者式、スレッドが安全ではない
    public class Singleton1 {
        private static Singleton1 instance;
        private Singleton1() {}
        public static Singleton1 getInstance() {
            if (instance == null) {
                instance = new Singleton1();
            }
            return instance;
        }
    }
    

    怠け者は怠け者で、最初の呼び出し時に初期化され、メモリの浪費を回避します.この方式は最も基本的な実装方式であり、この実装はマルチスレッドをサポートしない.synchronizedがロックされていないため、マルチスレッドでは正常に動作しません.
    2、怠け者式、スレッド安全
    public class Singleton2 {
        private static Singleton2 instance;
        private Singleton2() {}
        public static synchronized Singleton2 getInstance() {
            if (instance == null) {
                instance = new Singleton2();
            }
            return instance;
        }
    }
    

    この方式では、第1の方式に同期ロックが付加され、マルチスレッドでよく動作することができるが、同期ロックは効率に影響し、多くの場合同期を必要としないため、効率は高くない.
    3、餓漢式
    public class Singleton3 {
        private static Singleton3 instance = new Singleton3();
        private Singleton3() {}
        public static Singleton3 getInstance() {
            return instance;
        }
    }
    

    この方式はクラスロード時に初期化され,ゴミオブジェクトが発生しやすく,メモリが浪費される.この方法はclassloderメカニズムに基づいてマルチスレッドの同期問題を回避するスレッドが安全である.
    4、ダブルチェックロック
    public class Singleton4 {
        private volatile static Singleton4 singleton;
        private Singleton4() {}
        public static Singleton4 getSingleton() {
            if (singleton == null) {
                synchronized (Singleton4.class) {
                    if (singleton == null) {
                        singleton = new Singleton4();
                    }
                }
            }
            return singleton;
        }
    }
    

    この方式はデュアルロック機構を採用し、スレッドは安全であり、マルチスレッドの場合、高性能を維持することができる.実はこれは第2の方式に対する強化です.
    5、静的内部クラス
    public class Singleton5 {
        private static class SingletonHolder {
            private static final Singleton5 INSTANCE = new Singleton5();
        }
        private Singleton5() {}
        public static final Singleton5 getInstance() {
            return SingletonHolder.INSTANCE;
        }
    }
    

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

    この実現方式はまるで神の書き方だ.マルチスレッド同期の問題を回避するだけでなく、シーケンス化メカニズムを自動的にサポートし、逆シーケンス化による新しいオブジェクトの再作成を防止し、複数回のインスタンス化を防止します.この方法はEffective Javaの著者Josh Blochが提唱した方法ですが、あまり使われていません.あなたも私も神ではありませんから.
    備考:一般的には、第1種は推奨されず、スレッドは安全ではありません.第2種の使用は推奨されず、効率が高くない.第3種の餓漢方式は、実行可能であるが、遅延負荷の効果がなく、メモリを浪費する第4種の二重検査ロックは、実行可能である.遅延ロード効果を明確に実現するには、5番目の静的内部クラスを使用します.逆シーケンス化に関連してオブジェクトを作成する場合は、6番目の列挙方法を使用します.