web-dev-qa-db-ja.com

Javaでシングルトンパターンを実装するための効率的な方法は何ですか?

Javaでシングルトンパターンを実装するための効率的な方法は何ですか?

765

列挙型を使う:

public enum Foo {
    INSTANCE;
}

Joshua Blochは、Google I/O 2008での彼の Effective Java Reloaded 講演でこのアプローチを説明しました: ビデオへのリンク 。彼のプレゼンテーションのスライド30-32( effective_Java_reloaded.pdf )も見てください。

直列化可能シングルトンを実装する正しい方法

public enum Elvis {
    INSTANCE;
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}

編集: An "有効なJava"のオンライン部分

「このアプローチは、より簡潔で、シリアル化の仕組みを無料で提供し、洗練されたシリアル化やリフレクション攻撃に直面したとしても、多重インスタンス化に対する確固たる保証を提供することを除いて、パブリックフィールドアプローチと機能的に同等です。まだ広く採用されていませんが、 単一要素のenum型がシングルトン を実装するための最良の方法です。 "

753
Stephen Denne

使い方によっては、いくつかの「正しい」答えがあります。

Java 5以降では、enumを使用するのが最善の方法です。

public enum Foo {
   INSTANCE;
}

Java 5より前の最も単純なケースは次のとおりです。

public final class Foo {

    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }

    public Object clone() throws CloneNotSupportedException{
        throw new CloneNotSupportedException("Cannot clone instance of this class");
    }
}

コードを見てみましょう。最初に、あなたはクラスが最終的になることを望みます。この場合、私はfinalキーワードを使用して、それが最終的なものであることをユーザーに知らせます。次に、ユーザーが自分でFooを作成できないようにするために、コンストラクターを非公開にする必要があります。コンストラクターから例外をスローすると、ユーザーはリフレクションを使用して2番目のFooを作成できなくなります。次に、唯一のインスタンスを保持するprivate static final Fooフィールドと、それを返すpublic static Foo getInstance()メソッドを作成します。 Java仕様では、クラスが最初に使用されたときにのみコンストラクターが呼び出されるようにしています。

非常に大きなオブジェクトや重い構築コードがあり、インスタンスが必要になる前に使用される可能性のある他のアクセス可能な静的メソッドまたはフィールドがある場合は、その後でのみ遅延初期化を使用する必要があります。

インスタンスをロードするためにprivate static classを使うことができます。その結果、コードは次のようになります。

public final class Foo {

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }
}

private static final Foo INSTANCE = new Foo();行はクラスFooLoaderが実際に使用されているときにのみ実行されるので、これは遅延インスタンス化の面倒を見て、スレッドセーフであることが保証されています。

オブジェクトをシリアル化できるようにしたい場合は、逆シリアル化によってコピーが作成されないようにする必要があります。

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

readResolve()メソッドは、あなたのプログラムの前回の実行でオブジェクトが直列化されていたとしても、唯一のインスタンスが返されることを保証します。

227
Roel Spilker

免責事項: すべての素晴らしい答えをまとめて私の言葉で書いたところです。


シングルトンの実装中に2つの選択肢があります
1。遅延読み込み
2。早期ロード

遅延ロードは多少のオーバーヘッド(正直に言うと)を追加するため、非常に大きなオブジェクトまたは重い構築コードがあり、インスタンスが必要になる前に使用される可能性のある他のアクセス可能な静的メソッドまたはフィールドがある場合にのみ使用します。あなたは遅延初期化を使用する必要があります。そうでなければ早めのロードを選択することは良い選択です。

シングルトンを実装する最も簡単な方法は、

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }
}

その初期ロードシングルトンを除いてすべてが良いです。遅延ロードシングルトンを試してみましょう

class Foo {

    // Our now_null_but_going_to_be sole hero 
    private static Foo INSTANCE = null;

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT  
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        // Creating only  when required.
        if (INSTANCE == null) {
            INSTANCE = new Foo();
        }
        return INSTANCE;
    }
}

これまでのところ非常に優れていますが、私たちのヒーローの多くのインスタンスを必要とする複数の邪悪なスレッドと一人で戦っている間私たちのヒーローは生き残れません。だから悪のマルチスレッドからそれを守ることができます

class Foo {

    private static Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        // No more tension of threads
        synchronized (Foo.class) {
            if (INSTANCE == null) {
                INSTANCE = new Foo();
            }
        }
        return INSTANCE;
    }
}

しかし、それは英雄を保護するのに十分ではない、本当に!これは私たちの英雄を助けるために私たちができる/するべき最善の方法です

class Foo {

    // Pay attention to volatile
    private static volatile Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        if (INSTANCE == null) { // Check 1
            synchronized (Foo.class) {
                if (INSTANCE == null) { // Check 2
                    INSTANCE = new Foo();
                }
            }
        }
        return INSTANCE;
    }
}

これを「ダブルチェックロッキングイディオム」と呼びます。不安定な記述を忘れるのは簡単で、なぜそれが必要なのか理解するのは難しいです。
詳細については http://www.cs.umd.edu/~pugh/Java/memoryModel/DoubleCheckedLocking.html

今、私たちは邪悪なスレッドについては確信していますが、残酷なシリアル化についてはどうですか?シリアル化解除中でも、新しいオブジェクトが作成されないようにする必要があります。

class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static volatile Foo INSTANCE = null;

    // Rest of the things are same as above

    // No more fear of serialization
    @SuppressWarnings("unused")
    private Object readResolve() {
        return INSTANCE;
    }
}

オブジェクトが前回のプログラムの実行でシリアル化された場合でも、メソッドreadResolve()は唯一のインスタンスが返されることを確認します。

最後に、スレッドとシリアル化に対する十分な保護を追加しましたが、コードは大きくて見苦しく見えます。私たちのヒーローにやり直させましょう

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    // Wrapped in a inner static class so that loaded only when required
    private static class FooLoader {

        // And no more fear of threads
        private static final Foo INSTANCE = new Foo();
    }

    // TODO add private shouting construcor

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    // Damn you serialization
    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

はい、これは私たちの同じヒーローです:)
private static final Foo INSTANCE = new Foo();はクラスFooLoaderが実際に使われているときにのみ実行されるので、これは遅延インスタンス化の面倒を見ます。

そしてそれはスレッドセーフであることが保証されていますか。

そして、私たちはこれまでに来ました、これが私たちがしたすべてを達成するための最善の方法は最善の方法です

 public enum Foo {
       INSTANCE;
   }

内部的にどのように扱われます

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();
}

それだけで、シリアル化、スレッド、見苦しいコードを恐れなくなりました。また、 ENUMSシングルトンは遅延初期化されます です。

このアプローチは、より簡潔で、シリアル化の仕組みを無料で提供し、洗練されたシリアル化やリフレクション攻撃に直面したとしても、多重インスタンス化に対する確固たる保証を提供することを除けば、パブリックフィールドアプローチと機能的に同等です。このアプローチはまだ広く採用されていませんが、単一要素のenum型がシングルトンを実装するための最良の方法です。

-Joshua Bloch in "Effective Java"

これで、ENUMSがSingletonを実装するための最良の方法と見なされている理由に気づいたかもしれません。
私の blog でそれを更新しました。

130
xyz

Stu Thompsonによって投稿されたソリューションは、Java5.0以降で有効です。しかし、私はそれがエラーを起こしやすいと思うのでそれを使用しないほうがよいでしょう。

不安定な記述を忘れるのは簡単で、なぜそれが必要なのか理解するのは難しいです。揮発性がなければ、このコードはダブルチェックロックのアンチパターンのためにスレッドセーフではなくなります。これについての詳細は Java Concurrency in Practice の16.2.4項をご覧ください。つまり、このパターン(Java5.0より前またはvolatileステートメントなし)は、(まだ)誤った状態のBarオブジェクトへの参照を返す可能性があります。

このパターンはパフォーマンスの最適化のために考案されました。しかし、これは本当に本当の関心事ではなくなりました。次の遅延初期化コードは速く、そしてもっと重要なことに読みやすくなっています。

class Bar {
    private static class BarHolder {
        public static Bar bar = new Bar();
    }

    public static Bar getBar() {
        return BarHolder.bar;
    }
}
123
Benno Richters

Java 5以降でスレッドセーフ:

class Foo {
    private static volatile Bar bar = null;
    public static Bar getBar() {
        if (bar == null) {
            synchronized(Foo.class) {
                if (bar == null)
                    bar = new Bar(); 
            }
        }
        return bar;
    }
}

_ edit _ :ここでvolatile修飾子に注意してください。それがなければ、他のスレッドはその値の変化を見ることがJMM(Java Memory Model)によって保証されていないので、それは重要です。 同期はしません それを気にする - それはコードのそのブロックへのアクセスを直列化するだけです。

EDIT 2 :@Bnoの答えはBill Pugh(FindBugs)が推奨しているアプローチの詳細であり、議論の余地がある。読んで行き、彼の答えも投票してください。

94
Stu Thompson

怠惰な初期化を忘れてください 、問題が多すぎます。これが最も簡単な解決策です。

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}
90
Jonathan

本当に必要なことを確認してください。それに対するいくつかの議論を見るために "シングルトンアンチパターン"のグーグルをしてください。それには本質的に問題はありませんが、グローバルなリソースやデータを公開するためのメカニズムにすぎないので、これが最善の方法であることを確認してください。特に、DIはテスト目的でモックされたリソースを使用できるため、依存性注入が単体テストも使用している場合に特に便利です。

47
Neil Burroughs

シングルトンは、それをロードしたクラスローダーのシングルトンにすぎません。あなたが複数のローダー(コンテナ)を使用しているならば、それぞれが独自のシングルトンのバージョンを持つことができます。

26
Javamann

シングルトンを使用する代わりにDIを提案するいくつかの回答に私は誤解しています。これらは無関係の概念です。 DIを使用して、シングルトンインスタンスまたは非シングルトンインスタンス(スレッドごと)のいずれかをインジェクトでき​​ます。少なくともSpring 2.xを使用している場合、これは当てはまります。私は他のDIフレームワークについて話すことはできません。

したがって、OPに対する私の答えは、(最も些細なサンプルコードを除いて)次のようになります。

  1. SpringのようなDIフレームワークを使って
  2. 依存関係がシングルトンか、リクエストスコープか、セッションスコープかなど、DI構成の一部にします。

このアプローチでは、シングルトンを使用するかどうかは簡単に元に戻すことができる実装詳細(もちろん、使用するシングルトンがスレッドセーフである場合)で、分離された(したがって柔軟でテスト可能な)アーキテクチャが得られます。

21
Andrew Swan

それを書く前に、なぜシングルトンが必要なのかを本当に考えてください。それらを使用することについての疑似宗教的な議論があります。あなたがJavaでシングルトンをグーグルするならば、あなたはかなり容易につまずくことができます。

個人的には、シングルトンをできるだけ頻繁に避けるようにしていますが、その多くはシングルトンをグーグルすることで見つけることができます。シングルトンは誰もが理解しやすいので濫用されることが非常に多く、「グローバル」データをOOデザインに入れるメカニズムとして使用されていますオブジェクトのライフサイクル管理を迂回する(または実際にBの内側からAを実行する方法について考える)。 Niceミドルグラウンドについては、制御の反転(IoC)や依存性注入(DI)などを検討してください。

もし本当に必要なのなら、ウィキペディアにシングルトンの適切な実装の良い例があります。

20
Aidos

以下は3つの異なるアプローチです

1)Enum

/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
    INSTANCE;
}

2)ダブルチェックロッキング/レイジーローディング

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private static volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public static DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

3)静的な工場メソッド

/**
* Singleton pattern example with static factory method
*/

public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}
16
Abhijit Gaikwad

私は自分のシングルトンを管理するためにSpring Frameworkを使います。それはクラスの「シングルトン性」を強制しません(複数のクラスローダーが関係している場合は絶対にできません)が、さまざまなタイプのオブジェクトを作成するためのさまざまなファクトリを構築および構成する本当に簡単な方法を提供します。

13
Matt

バージョン1:

public class MySingleton {
    private static MySingleton instance = null;
    private MySingleton() {}
    public static synchronized MySingleton getInstance() {
        if(instance == null) {
            instance = new MySingleton();
        }
        return instance;
    }
}

遅延ロード、ブロッキング付きスレッドセーフ、synchronizedのための低パフォーマンス。

バージョン2:

public class MySingleton {
    private MySingleton() {}
    private static class MySingletonHolder {
        public final static MySingleton instance = new MySingleton();
    }
    public static MySingleton getInstance() {
        return MySingletonHolder.instance;
    }
}

遅延ロード、ノンブロッキング、高性能のスレッドセーフ。

11
coderz

ウィキペディアにはいくつかの のシングルトンがあり、これもJavaです。 Java 5の実装はかなり完成度が高く、スレッドセーフです(ダブルチェックロッキングが適用されます)。

10
macbirdie

あなたが遅延読み込みを必要としていない場合は、単に試してみてください

public class Singleton {
    private final static Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() { return Singleton.INSTANCE; }

    protected Object clone() {
        throw new CloneNotSupportedException();
    }
}

遅延ロードが必要で、シングルトンをスレッドセーフにしたい場合は、ダブルチェックパターンを試してください。

public class Singleton {
        private static Singleton instance = null;

        private Singleton() {}

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

        protected Object clone() {
            throw new CloneNotSupportedException();
        }
}

二重チェックパターンが機能することは保証されていないため(コンパイラの問題のため、これ以上のことはわかりません)、getInstanceメソッド全体を同期したり、すべてのシングルトン用のレジストリを作成することもできます。

10
Aleksi Yrttiaho

これについてはもう少し遅れるかもしれませんが、シングルトンの実装には多くのニュアンスがあります。ホルダーパターンは多くの状況では使用できません。そしてIMOはvolatileを使うとき - ローカル変数も使うべきです。最初から始めて、問題について繰り返しましょう。私の言っていることがわかります。


最初の試みは次のようになります。

public class MySingleton {

     private static MySingleton INSTANCE;

     public static MySingleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new MySingleton();
        }

        return INSTANCE;
    }
    ...
}

ここには、INSTANCEというプライベートな静的メンバーと、getInstance()というパブリックな静的メソッドを持つMySingletonクラスがあります。初めてgetInstance()が呼び出されたとき、INSTANCEメンバはnullです。フローは作成条件に入り、MySingletonクラスの新しいインスタンスを作成します。その後getInstance()を呼び出すと、INSTANCE変数がすでに設定されていることがわかります。したがって、別のMySingletonインスタンスは作成されません。これにより、getInstance()のすべての呼び出し元で共有されるMySingletonのインスタンスが1つだけになります。

しかし、この実装には問題があります。マルチスレッドアプリケーションは、単一インスタンスの作成に関して競合状態になります。複数の実行スレッドが同時に(またはその周辺で)getInstance()メソッドをヒットした場合、それぞれのスレッドはINSTANCEメンバをnullと見なします。これにより、各スレッドは新しいMySingletonインスタンスを作成し、続いてINSTANCEメンバーを設定します。


private static MySingleton INSTANCE;

public static synchronized MySingleton getInstance() {
    if (INSTANCE == null) {
        INSTANCE = new MySingleton();
    }

    return INSTANCE;
}

ここでは、メソッドシグネチャにsynchronizedキーワードを使用して、getInstance()メソッドを同期しました。これは確かに我々の競争状態を修正するでしょう。スレッドは一度に1つずつブロックしてメソッドに入ります。しかし、それはパフォーマンスの問題も引き起こします。この実装は単一インスタンスの作成を同期させるだけでなく、読み取りを含むgetInstance()へのすべての呼び出しを同期させます。読み取りはINSTANCEの値を返すだけなので、読み取りを同期させる必要はありません。読み込みは私たちの呼び出しの大部分を占めるので(インスタンス化は最初の呼び出しでのみ起こることを覚えておいてください)、メソッド全体を同期させることで不必要なパフォーマンスヒットを招きます。


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronize(MySingleton.class) {
            INSTANCE = new MySingleton();
        }
    }

    return INSTANCE;
}

ここでは、同期をメソッドシグネチャからMySingletonインスタンスの作成をラップするsynchronizedブロックに移動しました。しかし、これで問題は解決しましたか?さて、私たちはもはや読み取りをブロックしていませんが、私たちは一歩後退しました。複数のスレッドが同時にまたはほぼ同時にgetInstance()メソッドをヒットし、それらはすべてINSTANCEメンバーをnullと見なします。それからそれらは、ロックを取得しインスタンスを作成する同期ブロックにヒットします。そのスレッドがブロックを出ると、他のスレッドがロックを奪い合い、それぞれのスレッドが1つずつブロックを通過してクラスの新しいインスタンスを作成します。だから我々は我々が始めたところに戻ってきました。


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

ここで、ブロックの内側から別のチェックを発行します。 INSTANCEメンバーが既に設定されている場合は、初期化をスキップします。これはダブルチェックロッキングと呼ばれます。

これは多重インスタンス化の問題を解決します。しかし、もう一度言いますが、私たちの解決策には別の課題があります。他のスレッドは、INSTANCEメンバーが更新されたことを「認識」しないかもしれません。これは、Javaがメモリ操作を最適化する方法によるものです。スレッドは、元の変数の値をメインメモリからCPUのキャッシュにコピーします。その後、値の変更はそのキャッシュに書き込まれ、そこから読み取られます。これはパフォーマンスを最適化するように設計されたJavaの機能です。しかし、これは私たちのシングルトン実装に問題を引き起こします。別のキャッシュを使用して、別のCPUまたはコアによって処理されている2番目のスレッドは、最初のスレッドによって行われた変更を認識しません。これにより、2番目のスレッドはINSTANCEメンバをnullとして認識し、シングルトンの新しいインスタンスを強制的に作成します。


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

INSTANCEメンバーの宣言にvolatileキーワードを使用してこれを解決します。これは、CPUキャッシュではなくメインメモリから常に読み書きするようにコンパイラに指示します。

しかし、この単純な変更にはコストがかかります。私たちはCPUキャッシュを迂回しているので、揮発性のINSTANCEメンバを操作するたびにパフォーマンスが低下します - これは4回です。存在(1と2)を再確認し、値(3)を設定してから値(4)を返します。メソッドの最初の呼び出し中にインスタンスを作成するだけなので、このパスはフリンジケースであると主張することができます。おそらく、パフォーマンスの低下は許容範囲内です。しかし、私たちの主なユースケースであるreadでも、volatileメンバーを2回操作します。一度存在を確認するために、そしてまたその値を返すために。


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    MySingleton result = INSTANCE;
    if (result == null) {
        synchronized(MySingleton.class) {
            result = INSTANCE;
            if (result == null) {
                INSTANCE = result = createInstance();
            }
        }
    }

    return result;
}

パフォーマンスへの影響はvolatileメンバーを直接操作することによるものなので、ローカル変数をvolatileの値に設定し、代わりにローカル変数を操作しましょう。これにより、揮発性物質を操作する回数が減り、失われたパフォーマンスの一部が取り戻されます。 synchronizedブロックに入ったときに、ローカル変数をもう一度設定しなければならないことに注意してください。これにより、ロックを待っている間に発生したすべての変更が確実に最新になります。

私は最近これについての記事を書きました。 シングルトンの分解 。これらの例とそこにある「ホルダー」パターンの例に関する詳細情報を見つけることができます。二重チェックされた揮発性アプローチを紹介する現実世界の例もあります。お役に立てれば。

8
Michael Andrews

私はEnumシングルトンと言うだろう

Javaでenumを使用するシングルトンは、通常、enumシングルトンを宣言する方法です。列挙型シングルトンはインスタンス変数とインスタンスメソッドを含むことができます。わかりやすくするために、オブジェクトの状態に影響を与えるのであれば、そのメソッドのスレッドセーフ性を保証する必要がある以上のインスタンスメソッドを使用している場合も注意してください。

列挙型の使用は非常に実装が簡単で、他の方法で回避する必要がある直列化可能オブジェクトに関しては問題ありません。

/**
* Singleton pattern example using Java Enum
*/
public enum Singleton {
        INSTANCE;
        public void execute (String arg) {
                //perform operation here
        }
}

SingletonでgetInstance()メソッドを呼び出すよりもはるかに簡単に、Singleton.INSTANCEでアクセスできます。

1.12列挙型定数のシリアル化

列挙型定数は、通常のシリアライズ可能オブジェクトまたは外部化可能オブジェクトとは異なる方法でシリアライズされます。列挙型定数の直列化形式は、その名前だけで構成されています。定数のフィールド値はフォームに存在しません。列挙型定数をシリアル化するために、ObjectOutputStreamは列挙型定数のnameメソッドによって返された値を書き込みます。列挙型定数を逆シリアル化するために、ObjectInputStreamはストリームから定数名を読み取ります。次に、Java.lang.Enum.valueOfメソッドを呼び出して、受け取った定数の名前とともに引数として定数の列挙型を渡して、逆シリアル化された定数を取得します。他の直列化可能オブジェクトまたは外部化可能オブジェクトと同様に、enum定数は、直列化ストリーム内に後で現れる後方参照のターゲットとして機能できます。

Enum定数をシリアル化するプロセスはカスタマイズできません。列挙型で定義されたクラス固有のwriteObjectreadObjectreadObjectNoDatawriteReplace、およびreadResolveメソッドは、シリアル化および逆シリアル化中に無視されます。同様に、serialPersistentFieldsまたはserialVersionUIDフィールド宣言も無視されます - すべてのenum型は固定のserialVersionUID0Lになります。列挙型の直列化可能フィールドとデータを文書化することは不要です。送信されるデータの種類には違いがないからです。

Oracleのドキュメントから引用

従来のシングルトンのもう1つの問題は、Serializableインターフェースを一度実装すると、readObject()メソッドは常にJavaのコンストラクタのような新しいインスタンスを返すため、シングルトンのままではなくなることです。これは、readResolve()を使用し、以下のようにシングルトンに置き換えることによって新しく作成されたインスタンスを破棄することによって回避することができます。

 // readResolve to prevent another instance of Singleton
 private Object readResolve(){
     return INSTANCE;
 }

一時的にする必要があるため、シングルトンクラスが状態を維持する場合、これはさらに複雑になる可能性がありますが、Enum Singletonでは、シリアライゼーションはJVMによって保証されます。


お読みください

  1. シングルトンパターン
  2. 列挙、シングルトン、およびデシリアライゼーション
  3. ダブルチェックロッキングとシングルトンパターン
8
NullPoiиteя
There are 4 ways to create a singleton in Java.

1- eager initialization singleton

    public class Test{
        private static final Test test = new Test();
        private Test(){}
        public static Test getTest(){
            return test;
        }
    }

2- lazy initialization singleton (thread safe)

    public class Test {
         private static volatile Test test;
         private Test(){}
         public static Test getTest() {
            if(test == null) {
                synchronized(Test.class) {
                    if(test == null){test = new Test();
                }
            }
         }

        return test;
    }


3- Bill Pugh Singleton with Holder Pattern (Preferably the best one)

    public class Test {

        private Test(){}

        private static class TestHolder{
            private static final Test test = new Test();
        }

        public static Test getInstance(){
            return TestHolder.test;
        }
    }

4- enum singleton
      public enum MySingleton {
        INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}
7
Dheeraj Sachan

これは、単純なsingletonを実装する方法です。

public class Singleton {
    // It must be static and final to prevent later modification
    private static final Singleton INSTANCE = new Singleton();
    /** The constructor must be private to prevent external instantiation */ 
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

これはsingletonを適切に遅延作成する方法です。

public class Singleton {
    // The constructor must be private to prevent external instantiation   
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    /** 
     * The static inner class responsible for creating your instance only on demand,
     * because the static fields of a class are only initialized when the class
     * is explicitly called and a class initialization is synchronized such that only 
     * one thread can perform it, this rule is also applicable to inner static class
     * So here INSTANCE will be created only when SingletonHolder.INSTANCE 
     * will be called
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}
4
Nicolas Filotto

クラスのインスタンス変数を遅延的にロードする必要がある場合は、 double-checking idiomが必要です。静的変数またはシングルトンを遅延的にロードする必要がある場合は、 初期化オンデマンドホルダー イディオムが必要です。

さらに、シングルトンを直列化可能にする必要がある場合は、他のすべてのフィールドを一時的にし、シングルトンオブジェクトを不変に保つためにreadResolve()メソッドを実装する必要があります。それ以外の場合は、オブジェクトが逆シリアル化されるたびに、オブジェクトの新しいインスタンスが作成されます。 readResolve()が実行するのはreadObject()によって読み取られた新しいオブジェクトを置き換えることです。これにより、新しいオブジェクトを参照する変数がないため、その新しいオブジェクトはガベージコレクションされることになります。

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // original singleton instance.
} 
3
Onur

シングルトンオブジェクトを作る様々な方法:

  1. Joshua Blochによると - Enumは最高でしょう。

  2. ダブルチェックロッキングも使えます。

  3. 内部静的クラスでさえも使用できます。

3

列挙シングルトン

スレッドセーフなシングルトンを実装する最も簡単な方法は、Enumを使うことです。

public enum SingletonEnum {
  INSTANCE;
  public void doSomething(){
    System.out.println("This is a singleton");
  }
}

このコードは、Java 1.5でEnumが導入されてから機能します。

ダブルチェックロック

マルチスレッド環境(Java 1.5以降)で動作する「クラシック」シングルトンをコーディングしたい場合は、これを使用してください。

public class Singleton {

  private static volatile Singleton instance = null;

  private Singleton() {
  }

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

Volatileキーワードの実装が異なるため、1.5より前ではこれはスレッドセーフではありません。

Early loading Singleton(Java 1.5より前でも動作します)

この実装は、クラスがロードされたときにシングルトンをインスタンス化し、スレッドセーフを提供します。

public class Singleton {

  private static final Singleton instance = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return instance;
  }

  public void doSomething(){
    System.out.println("This is a singleton");
  }

}
3
Dan Moldovan

JSE 5.0以上ではEnumアプローチを取り、そうでなければ静的シングルトンホルダーアプローチ(Bill Pughによって記述されている遅延ローディングアプローチ)を使用してください。

2
raoadnan

シングルトンに対してよく使用されるもう1つの議論は、テスト容易性の問題です。シングルトンはテスト目的のために簡単にあざけることはできません。これが問題になる場合は、次のように少し修正します。

public class SingletonImpl {

    private static SingletonImpl instance;

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

    public static void setInstance(SingletonImpl impl) {
        instance = impl;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

追加されたsetInstanceメソッドにより、テスト中にシングルトンクラスのモックアップ実装を設定できます。

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

これは初期の初期化アプローチでも機能します。

public class SingletonImpl {

    private static final SingletonImpl instance = new SingletonImpl();

    private static SingletonImpl alt;

    public static void setInstance(SingletonImpl inst) {
        alt = inst;
    }

    public static SingletonImpl getInstance() {
        if (alt != null) {
            return alt;
        }
        return instance;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

これには、この機能を通常のアプリケーションにも公開するという欠点があります。そのコードに取り組んでいる他の開発者は、特定の関数を変更してアプリケーション全体の振る舞いを変更するために「setInstance」メソッドを使用したくなるかもしれません。

それでも、(必要なときに)モックアップテストを行う可能性があるため、このコードの公開は許容できる代償になるかもしれません。

2
user3792852

最も単純なシングルトンクラス

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}
1
rohan kamat

Java 1.5以降、enumは、利用可能な最高のシングルトン実装であり、マルチスレッド環境でも1つのインスタンスしか作成されないことを保証しているので、私はまだ考えています。

public enum Singleton{ INSTANCE; }

そしてこれで終わりです!

0
shikjohari

この記事を見てください。

JavaのコアライブラリにおけるGoFデザインパターンの例

ベストアンサーの "シングルトン"セクションから、

シングルトン(毎回同じインスタンスを返す作成メソッドによって毎回認識可能)

  • Java.lang.Runtime#getRuntime()
  • Java.awt.Desktop#getDesktop()
  • Java.lang.System#getSecurityManager()

Javaネイティブクラス自体からシングルトンの例を学ぶこともできます。

0
itiskj

私が今まで見た中で最高のシングルトンパターンはSupplierインターフェースを使います。

  • それは一般的で再利用可能です
  • 遅延初期化をサポート
  • 初期化されるまで同期されるだけで、ブロッキングサプライヤは非ブロッキングサプライヤに置き換えられます。

下記参照:

public class Singleton<T> implements Supplier<T> {

    private boolean initialized;
    private Supplier<T> singletonSupplier;

    public Singleton(T singletonValue) {
        this.singletonSupplier = () -> singletonValue;
    }

    public Singleton(Supplier<T> supplier) {
        this.singletonSupplier = () -> {
            // The initial supplier is temporary; it will be replaced after initialization
            synchronized (supplier) {
                if (!initialized) {
                    T singletonValue = supplier.get();
                    // Now that the singleton value has been initialized,
                    // replace the blocking supplier with a non-blocking supplier
                    singletonSupplier = () -> singletonValue;
                    initialized = true;
                }
                return singletonSupplier.get();
            }
        };
    }

    @Override
    public T get() {
        return singletonSupplier.get();
    }
}
0
user1024314