Javaでシングルトンパターンを実装する効率的な方法は何ですか?[閉まっている]


812

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


1
「Javaでシングルトンパターンを実装する効率的な方法は何ですか?」効率的に定義してください。
MarianPaździoch16年

medium.com/@kevalpatel2106/…。これは、シングルトンパターンでスレッド、リフレクション、シリアル化の安全性を実現する方法に関する完全な記事です。これは、シングルトンクラスの利点と制限を理解するための優れた情報源です。
Keval Patel 2016

Joshua BlochがEffective Javaで指摘しているように、列挙型シングルトンが最善の方法です。ここで私は怠け者/熱心等の様々な実装形態に分類している
isaolmez

回答:


782

列挙型を使用します。

public enum Foo {
    INSTANCE;
}

ジョシュアブロッホは彼にこのアプローチを説明した効果的なJavaのリローデッドの GoogleのI / O 2008で講演:ビデオへのリンクを。彼のプレゼンテーションのスライド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));
    }
}

編集:「Effective Java」オンライン部分は言う:

「このアプローチは、パブリックフィールドアプローチと機能的に同等ですが、より簡潔であり、シリアル化機構を無料で提供し、洗練されたシリアル化またはリフレクション攻撃に直面しても、複数のインスタンス化に対して強力な保証を提供します。このアプローチはまだ広く採用されていないので、単一要素の列挙型がシングルトンを実装する最良の方法です。」


203
人々は列挙型を機能のある単なるクラスと見なすべきだと思います。コンパイル時にクラスのインスタンスをリストできる場合は、列挙型を使用してください。
Amir Arad、

7
個人的には、シングルトンパターンを直接使用する必要性はあまりありません。私はときどき、スプリングの依存性注入を、それがシングルトンと呼ぶものを含むアプリケーションコンテキストで使用します。私のユーティリティクラスには静的メソッドのみが含まれる傾向があり、それらのインスタンスは必要ありません。
Stephen Denne、

3
こんにちは、このタイプのシングルトンをモックしてテストケースでテストする方法を誰かに教えてもらえますか?このタイプの偽のシングルトンインスタンスを交換しようとしましたが、できませんでした。
Ashish Sharma

29
それは理にかなっていると思いますが、それでも好きではありません。別のクラスを拡張するシングルトンをどのように作成しますか?列挙型を使用する場合は使用できません。
chharvey、2009

11
@bvdb:多くの柔軟性が必要な場合は、そもそもシングルトンを実装することですでに失敗しています。必要なときに独立したインスタンスを作成する機能は、それ自体かなり高額です。
cHao 2013

233

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

java5以来、それを行う最良の方法は列挙型を使用することです:

public enum Foo {
   INSTANCE;
}

java5以前では、最も単純なケースは次のとおりです。

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()は、オブジェクトがプログラムの前回の実行でシリアル化された場合でも、唯一のインスタンスが返されることを確認します。


32
リフレクションのチェックは無意味です。他のコードがプライベートでリフレクションを使用している場合、それはゲームオーバーです。このような誤用のもとで正しく機能しようとする理由さえありません。とにかく、それはとにかく不完全な「保護」であり、無駄なコードがたくさんあるだけです。
Wouter Coekaerts、2009

5
>「最初に、クラスを最終的にしたい」。誰かがこれについて詳しく説明してもらえますか?
PlagueHammer 2009年

2
逆シリアル化の保護は完全に壊れています(これは、Effective Java 2nd Edで言及されていると思います)。
トム・ホーティン-2009

9
-1これは絶対に最も単純なケースではなく、考案されたものであり、不必要に複雑です。すべてのケースの99.9%で十分である実際に最も単純なソリューションについては、ジョナサンの答えを見てください。
Michael Borgwardt 09/09/30

2
これは、シングルトンがスーパークラスから継承する必要がある場合に役立ちます。enumはスーパークラスを持つことができないため、この場合はenumシングルトンパターンを使用できません(ただし、インターフェイスを実装できます)。パターンシングルトン列挙型を使用できないときにたとえば、Googleのグアバは、静的最終フィールドを使用しています:code.google.com/p/guava-libraries/source/browse/trunk/guava/src/...
エティエンヌ・ヌヴー

139

免責事項:私はすべての素晴らしい答えを要約し、私の言葉でそれを書きました。


シングルトンを実装する場合、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シングルトンは遅延初期化されます

このアプローチは、機能がパブリックフィールドアプローチと同等ですが、より簡潔であり、シリアル化機構を無料で提供し、洗練されたシリアル化またはリフレクション攻撃に直面しても、複数のインスタンス化に対する強力な保証を提供します。このアプローチはまだ広く採用されていませんが、単一要素の列挙型がシングルトンを実装する最良の方法です。

-「Effective Java」のJoshua Bloch

これで、ENUMSがシングルトンを実装する最良の方法と見なされている理由に気付いたかもしれません。忍耐力に感謝します:)
私のブログで更新しました。


3
明確化:enumを使用して実装されたシングルトンは遅延して初期化されます。詳細はこちら:stackoverflow.com/questions/16771373/...

2
素晴らしい答え。最後に、例外をスローするようにcloneメソッドをオーバーライドします。
riship89 2014年

2
@xyzいい説明、私は本当に楽しかったし、非常に簡単に学んだし、これを忘れないように願っています
Premraj

1
私がこれまでにstackoverflowで得た最良の答えの1つ。ありがとう!
Shay Tsadok 2017年

1
そこシングルトンとして列挙型を使用したとの直列化の問題は:任意のメンバフィールドの値がされていないシリアライズため、復元されません。Java Object Serialization Specification、version 6.0を参照してください。別の問題:バージョン管理なし—すべての列挙型にはの固定がserialVersionUIDあり0Lます。3番目の問題:カスタマイズなし:列挙型によって定義されたクラス固有のwriteObject、readObject、readObjectNoData、writeReplace、およびreadResolveメソッドは、シリアライズおよびデシリアライズ時に無視されます。
バジルブルク

124

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

揮発性のステートメントを忘れがちであり、なぜそれが必要なのかを理解するのは困難です。volatileがないと、ダブルチェックされたロックのアンチパターンのため、このコードはスレッドセーフではなくなります。これについて詳しくは、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;
    }
}

1
けっこうだ!私は揮発性に慣れていて、それを使用しています。おお、そしてJCiPのための3つの喝采。
Stu Thompson、

1
ああ、これはどうやらFindBugzの名声のWilliam Pughによって提唱されたアプローチです。
Stu Thompson、

4
効果的なJavaの(2001年の著作権)の初版@Stu項目48の下で、このパターンの詳細を
Thiventパスカル

9
@Bno:コンストラクタをプライベートにするのはどうですか?
xyz 2013年

2
@ AlikElzin-kilakaわかりません。インスタンスはBarHolderのクラス読み込みフェーズ作成され、最初に必要になるまで遅延します。Barのコンストラクタは好きなだけ複雑にすることができますが、最初のまで呼び出されませんgetBar()。(ifとgetBar呼ばれている「早すぎる」、あなたは関係なくsingleonsが実装されている方法と同じ問題に直面していないでしょう。)あなたはここで、上記のコードの怠惰なクラスローディングを見ることができます: pastebin.com/iq2eayiR
チタンStrga

95

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;
    }
}

編集volatileここの修飾子に注意してください。:)それがなければ、他のスレッドがJMM(Javaメモリモデル)によってその値の変更を確認することが保証されないため、重要です。同期でその処理は行われず、コードブロックへのアクセスがシリアル化されるだけです。

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


1
volatile修飾子の詳細はどこで確認できますか?
eleven81


2
リフレクション攻撃について言及することは重要だと思います。ほとんどの開発者は心配する必要はありませんが、このような例(列挙型ベースのシングルトン)には、複数インスタンス化攻撃から保護するコードを含めるか、単にそのような可能性を示す免責事項を含める必要があるようです。
luis.espinal

2
ここでは揮発性キーワードは必要ありません。同期すると相互排除とメモリの可視性の両方が得られるためです。
Hemant 2013年

2
なぜJava 5+でこれすべてに悩むのですか?私の理解では、列挙型アプローチはスレッドセーフと遅延初期化の両方を提供します。それもはるかに単純です...さらに、列挙型を回避したい場合は、ネストされた静的クラスアプローチを引き続き使用します...
Alexandros

91

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

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

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

21
シングルトンインスタンス変数もfinalにすることができます。たとえば、プライベート静的最終Aシングルトン=新しいA();
jatanp 2008

19
静的なシングルトンはクラスが読み込まれるまでインスタンス化されず、クラスは必要になるまで読み込まれません(これは、最初にgetInstance()メソッドを参照した時点で適切です)。
Dan Dyer

7
静的をインスタンス化する前にクラスAがロードされる場合は、静的内部クラスに静的をラップして、クラスの初期化を切り離すことができます。
トム・ホーティン-タックライン2008

3
私はこの答えが最も単純で、アニルダンはインスタンスをfinalと宣言する必要がないことに同意します。静的メンバーが初期化されている間、他のスレッドはクラスにアクセスできません。これはコンパイラによって保証されます。つまり、すべての静的初期化は同期された方法で行われます-1つのスレッドのみ。
2014

4
このアプローチには1つの制限があります。コンストラクタは例外をスローできません。
wangf 2015

47

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


従来の方法でもモック値を注入できますが、標準/ srpingの方法ではないため、レガシーコードになるだけで余分な作業が発生します...
tgkprog

21

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

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

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

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


4
多分人々があなたに同意しないからです。私はあなたに反対票を投じていませんが、同意しません。DIを使用してシングルトンと同じ問題を解決できると思います。これは、「単一インスタンス」の理解に基づいており、「単一インスタンスのオブジェクト」だけではなく、「グローバルインスタンスから直接アクセスされる単一インスタンスのオブジェクト」を意味します。
トムアンダーソン、

2
これを少し拡張TicketNumbererするには、単一のグローバルインスタンスを必要とするとTicketIssuer、コード行を含むクラスをどこに書き込むかを検討しますint ticketNumber = ticketNumberer.nextTicketNumber();。従来のシングルトンの考え方では、前のコード行はのようにする必要がありますTicketNumberer ticketNumberer = TicketNumberer.INSTANCE;。DIの考え方では、クラスにはのようなコンストラクタがありpublic TicketIssuer(TicketNumberer ticketNumberer) { this.ticketNumberer = ticketNumberer; }ます。
トムアンダーソン

2
そして、そのコンストラクタを呼び出すことは他の誰かの問題になります。DIフレームワークは、ある種のグローバルマップでそれを行います。アプリのmainメソッド(またはそのミニオンの1つ)が依存関係を作成してからコンストラクターを呼び出すため、手作りのDIアーキテクチャがそれを行います。基本的に、グローバル変数(またはグローバルメソッド)の使用は、恐ろしいサービスロケーターパターンの単純な形式であり、そのパターンの他の使用と同様に、依存関係注入に置き換えることができます。
トムアンダーソン

@TomAndersonなぜ人々がサービスロケータパターンを「恐れる」のかについて私は本当に混乱しています。ほとんどの場合それはやり過ぎであるか、せいぜい必要ではないと思いますが、一見有用なケースがあります。パラメーターの数が少ない場合は、DIが確実に推奨されますが、20以上を想像してください。コードが構造化されていないと言っても有効な引数ではありません。これは、パラメーターのグループ化が意味をなさない場合があるためです。また、単体テストの観点からは、サービスのテストについては気にしません。サービスのビジネスロジックだけです。正しくコーディングされていれば、これは簡単です。私はこの必要性を非常に大規模なプロジェクトでのみ見ました。
ブランドンリン、

20

書く前に、なぜシングルトンが必要なのかをよく考えてください。Javaでシングルトンをグーグルする場合、簡単につまずくことができる、それらを使用することについての準宗教的な議論があります。

個人的には、多くの理由からシングルトンをできるだけ避けようとしますが、そのほとんどはシングルトンをグーグルすることで見つけることができます。シングルトンは、誰でも簡単に理解でき、「グローバルな」データをOO設計に取り込むためのメカニズムとして使用され、オブジェクトのライフサイクル管理を回避しやすいために使用されている(または、 AをBの中からどのように実行できるかを本当に考えています。制御の反転(IoC)や依存性注入(DI)などを見て、良い中間点を見つけてください。

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


同意した。これは、アプリケーションの残りの部分をキックスタートする基本クラスであり、それが複製された場合、完全な混乱(つまり、リソースへの単一のアクセスまたはセキュリティの強制)になります。アプリケーション全体でグローバルデータを渡すことは、大きなカップリングの赤信号です。本当に必要だと認めたときに使用してください。
サルバドールバレンシア

16

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

1)列挙型

/**
* 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;
    }
}

13

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


11

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


11

バージョン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;
    }
}

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


10

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

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メソッド全体を同期するか、すべてのシングルトンのレジストリを作成することもできます。


2
最初のバージョンが最適です。クラスがシングルトンを提供する以外に何も行わないと仮定すると、通常、クラスの遅延読み込みにより、2番目のバージョンとほぼ同じ時点でインスタンス化されます。
Dan Dyer

1
静的の場合、ダブルチェックは無意味です。そして、なぜ保護されたクローンメソッドを公開したのですか?
トム・ホーティン-

1
-1お使いのダブルチェックロックのバージョンが壊れています。
アッシリア2012

5
また、シングルトン変数を作成する必要がありますvolatile
MyTitle

最初のバージョンレイジーでスレッドセーフです。
Miha_x64

9

イナムシングルトン

Javaで列挙型を使用するシングルトンは、一般的に列挙型シングルトンを宣言する方法です。列挙型シングルトンには、インスタンス変数とインスタンスメソッドを含めることができます。簡単にするために、インスタンスメソッドを使用している場合は、オブジェクトの状態に影響を与える場合にそのメソッドのスレッドセーフを確保する必要があることにも注意してください。

enumの使用は実装が非常に簡単であり、他の方法で回避する必要があるシリアル化可能なオブジェクトに関する欠点はありません。

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

あなたはでそれにアクセスすることができますSingleton.INSTANCEはるかに簡単に呼び出すよりも、getInstance()シングルトンにする方法を。

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

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

任意のクラス固有の:enum定数が直列化されるプロセスは、カスタマイズすることができないwriteObjectreadObjectreadObjectNoDatawriteReplace、およびreadResolve列挙タイプによって定義された方法は、シリアライゼーションおよびデシリアライゼーションの際に無視されます。同様に、任意のserialPersistentFieldsor serialVersionUIDフィールド宣言も無視されます。すべての列挙型にはの固定がserialVersionUIDあり0Lます。送信されるデータのタイプに変化がないため、列挙型のシリアライズ可能なフィールドとデータを文書化する必要はありません。

Oracleドキュメントから引用

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

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

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


良い読み

  1. シングルトンパターン
  2. 列挙型、シングルトン、逆シリアル化
  3. ダブルチェックされたロックとシングルトンパターン

8
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");
    }
}

(1)は熱心ではなく、JVMクラスのロードメカニズムのために遅延です。
Miha_x64

@ Miha_x64熱心なローディングと言ったとき、私は熱心な初期化と言いました。
Dheeraj Sachan

効果的なJavaはすばらしい本ですが、間違いなく編集が必要です。
Miha_x64

@ Miha_x64熱心な読み込みとは何か、例で説明できます
Dheeraj Sachan

「熱心に」何かをすることは「できるだけ早く」を意味します。たとえば、Hibernateは明示的に必要な場合、関係のロードを熱心にサポートします。
Miha_x64

8

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


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

public class MySingleton {

     private static MySingleton INSTANCE;

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

        return INSTANCE;
    }
    ...
}

ここには、INSTANCEというプライベート静的メンバーを持つgetSingletonクラスと、getInstance()というパブリック静的メソッドがあります。最初に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インスタンスの作成をラップする同期ブロックに移動しました。しかし、これで問題は解決しますか?さて、読み取りをブロックすることはなくなりましたが、一歩後退しました。複数のスレッドが同時にまたはほぼ同時に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の機能です。しかし、これはシングルトンの実装に問題を引き起こします。2番目のスレッド(別のCPUまたはコアによって処理され、別のキャッシュを使用)は、最初のスレッドによる変更を認識しません。これにより、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)を返します。メソッドの最初の呼び出し中にのみインスタンスを作成するので、このパスはフリンジケースであると主張することができます。おそらく、作成時のパフォーマンスへの影響は許容できるでしょう。しかし、私たちの主なユースケースである読み取りでさえ、揮発性メンバーを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の値に設定し、代わりにローカル変数を操作してみましょう。これにより、揮発性の操作回数が減り、失われたパフォーマンスの一部を取り戻すことができます。同期ブロックに入るときに、ローカル変数を再度設定する必要があることに注意してください。これにより、ロックを待機している間に発生したすべての変更が最新の状態になります。

最近これについて記事を書きました。シングルトンの分解。これらの例の詳細と「ホルダー」パターンの例がそこにあります。また、ダブルチェックされた揮発性アプローチを紹介する実際の例もあります。お役に立てれば。


BearerToken instanceあなたの記事でなぜそうではないのstaticか説明していただけませんか?そしてそれは何result.hasExpired()ですか?
Woland 2017年

そして、どうでしょうclass MySingleton–多分それはそうあるべきfinalですか?
Woland 2017年

1
@Woland BearerTokenインスタンスはBearerTokenFactory 特定の認証サーバーで構成されている-の一部であるため、静的ではありません。多くのBearerTokenFactoryオブジェクトが存在する可能性があります-それぞれが独自の「キャッシュ」BearerTokenを持ち、期限が切れるまで渡されます。のhasExpired()メソッドBeraerTokenはファクトリのget()メソッドで呼び出され、期限切れのトークンを渡さないようにします。有効期限が切れている場合、新しいトークンが認証サーバーに要求されます。コードブロックに続く段落で、これについて詳しく説明します。
マイケルアンドリュース

4

これはシンプルな実装方法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();
    }
}

シングルトンから必要なのはそのインスタンスだけであると想定すると、どちらも遅延します。
Miha_x64

@ Miha_x64最初のケースは、JVMがクラスを初期化するときにシングルトンをインスタンス化し、2番目のケースはを呼び出すときにシングルトンのみをインスタンス化しますgetInstance()。しかし実際、クラスに他の静的メソッドSingletonがなく、呼び出すだけの場合getInstance()、実際の違いはありません。
Nicolas Filotto

3

あなたは必要とするダブルチェックあなたが遅延したクラスのインスタンス変数をロードする必要がある場合イディオムを。静的変数またはシングルトンをレイジーにロードする必要がある場合は、オンデマンドホルダーイディオムでの初期化が必要です

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

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

3

シングルトンオブジェクトを作成するさまざまな方法:

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

  2. ダブルチェックロックも使用できます。

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


3

列挙型シングルトン

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

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 ;
  }
}

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

シングルトンの早期ロード(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");
  }

}

2

JSE 5.0以降の場合はEnumアプローチを使用しますが、それ以外の場合は静的シングルトンホルダーアプローチを使用します(Bill Pughが説明するレイジーロードアプローチ)。


2

シングルトンに対してよく使用される別の引数は、テスト容易性の問題です。シングルトンは、テスト目的で簡単にモック可能ではありません。これが問題であることが判明した場合は、次のわずかな変更を加えます。

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」メソッドを使用して特定の関数を変更し、アプリケーション全体の動作を変更するように誘惑される可能性があります。

それでも、モックアップテストの可能性については(必要な場合)、このコードの露出は許容できる代償となる可能性があります。


1

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

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

1
これは、以下のジョナサンの回答と同じです
または14

1
5年前に投稿されたJonathanによるこの兄弟の回答の複製。興味深いコメントについては、その回答を参照してください。
バジルブルク2015年

0

Java 1.5以降でも、列挙型は、マルチスレッド環境でもインスタンスが1つだけ作成されることが保証されるため、利用可能なシングルトン実装の中で最も優れています。

public enum Singleton{ INSTANCE; }

そしてあなたは終わった!


1
これは、数年前に他の回答ですでに言及されています。
2016

0

この投稿をご覧ください。

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

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

シングルトン(毎回同じインスタンス(通常はそれ自体)を返す作成メソッドで認識可能)

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

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


0

私が今まで見た中で最高のシングルトンパターンは、サプライヤーインターフェイスを使用しています。

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

下記参照:

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();
    }
}

-3

単純な「static Foo foo = new Foo();」では不十分な場合があります。実行したい基本的なデータ挿入について考えてみてください。

一方、シングルトン変数をインスタンス化するメソッドを同期する必要があります。同期自体は悪くありませんが、パフォーマンスの問題やロックを引き起こす可能性があります(この例を使用する非常にまれな状況では。解決策は

public class Singleton {

    private static Singleton instance = null;

    static {
          instance = new Singleton();
          // do some of your instantiation stuff here
    }

    private Singleton() {
          if(instance!=null) {
                  throw new ErrorYouWant("Singleton double-instantiation, should never happen!");
          }
    }

    public static getSingleton() {
          return instance;
    }

}

今、何が起こりますか?クラスはクラスローダーを介してロードされます。クラスがバイト配列から解釈された直後に、VMは静的{} -ブロックを実行します。それが全体の秘密です:static-blockは、指定されたパッケージの指定されたクラス(名前)がこの1つのクラスローダーによってロードされるときに一度だけ呼び出されます。


3
違います。静的変数は、クラスが読み込まれるときに静的ブロックとともに初期化されます。宣言を分割する必要はありません。
クレイグP.モトリン2009年

-5
public class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton(){
    if (INSTANCE != null)
        throw new IllegalStateException (“Already instantiated...”);
}

    public synchronized static Singleton getInstance() { 
    return INSTANCE;

    }

}

getInstanceの前にSynchronizedキーワードを追加したため、2つのスレッドが同時にgetInstanceを呼び出す場合の競合状態を回避しました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.