12


7

Javaでシングルトンを記述するさまざまな方法

Javaでシングルトンを記述する古典的な方法は次のとおりです。

public class SingletonObject
{
    private SingletonObject()
    {
    }

    public static SingletonObject getSingletonObject()
    {
      if (ref == null)
          // it's ok, we can call this constructor
          ref = new SingletonObject();
      return ref;
    }

    private static SingletonObject ref;
}

マルチスレッドの場合に実行する必要がある場合は、synchronizedキーワードを追加できます。

しかし、私はそれを次のように書くことを好む:

public class SingletonObject
{
    private SingletonObject()
    {
        // no code req'd
    }

    public static SingletonObject getSingletonObject()
    {
      return ref;
    }

    private static SingletonObject ref = new SingletonObject();
}

私はもっ​​と簡潔だと思いますが、奇妙なことに、このように書かれたサンプルコードを見ませんでした。このようにコードを書いた場合、悪影響はありますか?

11 Answer


18


コードと「サンプルコード」の違いは、クラスのロード時にシングルトンがインスタンス化されるのに対して、「サンプル」バージョンでは実際に必要になるまでインスタンス化されないことです。


18


2番目のフォームでは、シングルトンが*熱心に*ロードされ、これが実際に優先されるフォームです(最初のフォームは、自分で言ったようにスレッドセーフではありません)。 積極的なロードは製品コードにとって悪いことではありませんが、Guiceの著者であるBob Leeがhttp://crazybob.org/2007/01/lazy-で議論しているように、シングルトンを遅延ロードしたいコンテキストがあります。以下に引用しているloading-singletons.html [Lazy Loading Singletons]:

_ _ まず、なぜシングルトンを遅延ロードしたいのですか? 本番環境では、通常、すべてのシングルトンを積極的にロードしてエラーを早期にキャッチし、パフォーマンスヒットを事前に取得しますが、テストおよび開発時には、時間を無駄にしないために絶対に必要なもののみをロードする必要があります。

Java 1.5より前は、単純だが効果的な単純な旧同期を使用してシングルトンを遅延ロードしました。

static Singleton instance;

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

1.5でのメモリモデルの変更により、悪名高いDouble-Checked Locking(DCL)イディオムが有効になりました。 DCLを実装するには、共通パスの「volatile」フィールドをチェックし、必要な場合にのみ同期します。

static volatile Singleton instance;

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

しかし、「volatile」は「synchronized」ほど高速ではなく、「synchronized」は最近非常に高速であり、DCLはより多くのコードを必要とするため、1.5がリリースされた後も、単純な古い同期を使用し続けました。

今日、Jeremy Mansonがhttp://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#dcl[Initialization on Demand Holder(IODH)イディオム]を指し示したときの驚きを想像してください。コードをほとんど必要とせず、同期オーバーヘッドがゼロです。 ゼロ。「volatile」よりも高速です。 IODHには、単純な古い同期と同じコード行数が必要であり、DCLよりも高速です。

IODHは、遅延クラスの初期化を利用します。 クラス内の何かに実際に触れるまで、JVMはクラスの静的初期化子を実行しません。 これは、ネストされた静的クラスにも適用されます。 次の例では、http://docs.oracle.com/javase/specs/jls/se5.0/html/execution.html#44557 [JLS保証] JVMは、誰かが getInstanceを呼び出すまで instance`を初期化しません。 () `:

static class SingletonHolder {
  static Singleton instance = new Singleton();
}

public static Singleton getInstance() {
  return SingletonHolder.instance;
}

{空の}[…​]

*更新:*クレジットの期限がある場合のクレジット、http://java.sun.com/docs/books/effective/ [Effective Java](copyright 2001)は、項目48でこのパターンを詳しく説明しました。 引き続き、非静的コンテキストで同期またはDCLを使用する必要があることを指摘します。

また、フレームワーク内のシングルトン処理を同期からDCLに切り替え、さらに10%パフォーマンスが向上しました(cglibの高速リフレクションを使用する前と比較して)。 マイクロベンチマークではスレッドを1つしか使用していなかったため、競合の激しいロックを比較的きめの細かい揮発性フィールドアクセスに置き換えると、同時実行性がさらに向上します。 _ _

Joshua Blochは(Effective Java、第2版以降)https://stackoverflow.com/users/56285/jonik[Jonik]で指摘されているように、単一要素の `enum`を使用してシングルトンを実装することを推奨しています。


10


後者の場合、シングルトンオブジェクトは必要になる前に作成されますが、ほとんどの場合、恐ろしく悪くはありません。

ところで、Joshua Blochは、(_ Effective Java_、2nd ed、item 3)単一要素列挙を使用してシングルトンを実装することを推奨しています。

public enum SingletonObject {
    INSTANCE;
}

彼は次の理由を説明します。

_ […​] it is more concise, provides serialization machinery for free, and 洗練されたシリアル化またはリフレクション攻撃に直面した場合でも、複数のインスタンス化に対する強固な保証を提供します。 このアプローチはまだ広く採用されていませんが、単一要素のenum型がシングルトンを実装するための最良の方法です。 _


5


実際、後者のコードはより標準的なパターンだと思います。 最初のバージョンはスレッドセーフではありません。 スレッドセーフにする方法には、すべてのアクセスでの同期、または_非常に慎重に_ダブルチェックロックを使用すること(Java 5メモリモデルの時点で安全である限り)が含まれます。

クラスが遅延して初期化されるため、インスタンスを作成せずにクラスで静的メソッドを呼び出した場合、後者のコードはオブジェクトを不必要に作成するだけです。

入れ子になったクラスを使用して初期化を実行するパターンがあります。これにより、この遅延が発生する可能性がありますが、個人的には、2番目のフォームはほとんどの場合、単独で十分です。

これについては、Effective Javaに詳細がありますが、アイテム番号を見つけることはできません。


2


あなたの問題は、シングルトンと遅延初期化を混合していることだと思います。 シングルトンは、*異なる初期化戦略*で実装できます。

  • クラスローディングの初期化

  • ダブルチェックロックを使用する遅延初期化

  • 単一チェックによる遅延初期化(可能性のある繰り返しで 初期化)

  • クラスローダーを使用する遅延初期化(ホルダークラスのイディオム)

これらのすべてのアプローチについては、http://books.google.com/books?id = ka2VUBqHiWkC&lpg = PP1&dq = bloch%20effective%20java&hl = de&pg = PT304#v = onepage&q =&f = false [Effective Java 2nd Item 71:Use lazy賢明な初期化]。


2


Javaでhttps://en.wikipedia.org/wiki/Singleton_pattern[singleton pattern]を実装するさまざまな方法は次のとおりです。

public class SingletonClass {
private SingletonClass= null;
public static SingletonClass getInstance() {
    if(SingletonClass != null) {
       SingletonClass = new SingletonClass();
    }
  }
}

これはスレッドセーフではありません。 以下は、シングルトンデザインパターンの「スレッドセーフ」実装です。

1. ドラコニアン同期

private static YourObject instance;

public static synchronized YourObject getInstance() {
    if (instance == null) {
        instance = new YourObject();
    }
    return instance;
}
  • 2。ダブルチェック同期*

private static final Object lock = new Object();
private static volatile YourObject instance;

public static YourObject getInstance() {
    YourObject r = instance;
    if (r == null) {
        synchronized (lock) {    // While we were waiting for the lock, another
            r = instance;        // thread may have instantiated the object.
            if (r == null) {
                r = new YourObject();
                instance = r;
            }
        }
    }
    return r;
}

3. 初期化オンデマンドホルダーイディオム

public class Something {
    private Something() {}

    private static class LazyHolder {
        static final Something INSTANCE = new Something();
    }

    public static Something getInstance() {
        return LazyHolder.INSTANCE;
    }
}

*4. もう1つはenum *を使用しています

public enum Singleton {
    SINGLE;
    public void myMethod(){
    }
}


1


2番目の方法は、マルチスレッドの問題を解決しますが、シングルトンが必要でない場合でも常に作成します。 最適な方法は、ネストされたクラスを作成することです。

public class singleton
{
    private singleton()
    {
       System.out.println("I'am called only when it's needed");
    }

    static class Nested
    {
       Nested() {}
       private static final singleton instance = new singleton();
    }

    public static singleton getInstance()
    {
       return Nested.instance;
    }

    public static void main(String [] args)
    {
      singleton.getInstance();
    }
}


0


シングルトンクラスを記述する最良の方法を以下に示します。試してみてください

public final class SingeltonTest {
    /**
     * @param args
     * @return
     */
    private static SingeltonTest instance = null;

    private SingeltonTest() {
        System.out.println("Rahul Tripathi");
    }

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


0


// Lazy loading enabled as well as thread safe

クラスシングルトン\ {

                private static class SingletonHolder {
                public static Singleton instance = new Singleton();
                }

                public static Singleton getInstance() {
                   return SingletonHolder.instance;
                  }
               }


0


プライベートコンストラクターと静的ブロックを宣言しているので、静的ブロックの目的は一度だけ実行され、静的ブロック内でコンストラクターが一度だけ呼び出されるようにオブジェクトを作成することです。

class Demo {
  static Demo d=null;

  static {
     d=new Demo();
  }

  private Demo(){
     System.out.println("Private Constructor");
  }

  void add(){
    System.out.println("Hello I am Non-Static");
  }

  static Demo getInstance(){
    return d;
  }

}