Help us understand the problem. What is going on with this article?

singletonの色んな実装方法を集めました。

More than 1 year has passed since last update.

Singleton実装種類

典型

もっとも典型的な書き方ですが、Non Thread Safeです。

public class Singleton1 {
    private static Singleton1 singleton = null;

    private Singleton1() {
        try {
            Thread.sleep(2 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static Singleton1 getInstance() {
        if (singleton == null) {
            singleton = new Singleton1();
        }
        return singleton;
    }
}
Non-Thread-Safe
ExecutorService executor = Executors.newFixedThreadPool(2);

Future<Singleton1> f1 = executor.submit(Singleton1::getInstance);
Future<Singleton1> f2 = executor.submit(Singleton1::getInstance);

Singleton1 s1 = f1.get();
Singleton1 s2 = f2.get();

System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);
異なるID
design.singleton.Singleton1@7ba4f24f
design.singleton.Singleton1@3b9a45b3
false

synchronized

典型的な書き方にsynchronizedを追加して、Thread Safeにします。
が、インスタンス生成後もsynchronizedのロックは解除できないので、性能面の懸念があります。

synchronized
public class Singleton2 {
    private static Singleton2 singleton = null;

    private Singleton2() {
        try {
            Thread.sleep(2 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static synchronized Singleton2 getInstance() {
        if (singleton == null) {
            singleton = new Singleton2();
        }
        return singleton;
    }
}

Double Check Lock

synchronizedの上に、もう一回Checkを追加することで、
synchronizedロックを回避したものです。

が、実は原子性の問題により、Thread Safeではないです!
詳細まで把握してないですが、new Singleton3()が幾つかに分解して実行されるからです。

DCL
public class Singleton3 {
    private static Singleton3 singleton = null;

    private Singleton3() {
        try {
            Thread.sleep(2 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static Singleton3 getInstance() {
        // 原子性によって、スレッドセーフではない
        if (singleton == null) {
            synchronized (Singleton3.class) {
                if (singleton == null) {
                    singleton = new Singleton3();
                }
            }
        }
        return singleton;
    }
}

volatile + DCL

volatileを使い、原始性を保証して、Double Check Lockを実装したものです。

volatile
public class Singleton4 {
    private static volatile Singleton4 singleton = null;

    private Singleton4() {
        try {
            Thread.sleep(2 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static Singleton4 getInstance() {
        // Volatileによって、原子性が保証される
        if (singleton == null) {
            synchronized (Singleton4.class) {
                if (singleton == null) {
                    singleton = new Singleton4();
                }
            }
        }

        return singleton;
    }
}

static fianl

クラスローダのおかげで、Thread Safeになり、書き方もシンプル。
が、リソースが無駄に使われる可能性がある。

static_final
public class Singleton5 {

    private static final Singleton5 singleton = new Singleton5();

    private Singleton5() {}

    public static Singleton5 getInstance() {
        return singleton;
    }
}

Holder

static finalでThread Safeにしながら、遅延生成してリソースも無駄にしない実装です。

Holder
public class Singleton6 {

    private static class SingletonHolder {
        private static final Singleton6 singleton = new Singleton6();
        private SingletonHolder() { }
    }

    private Singleton6() {
    }

    public static Singleton6 getInstance() {
        return SingletonHolder.singleton;
    }
}

enum

enumを利用する。
が、リソースが無駄に使われる可能性がある。

enum
public enum Singleton7 {
    SINGLETON;
}

本質は、static finalとあまり変わらないですが、publicであること。
public static final Singleton7 SINGLETON = new Singleton7();

まとめ

実装方法 無駄リソース スレッド安全 スレッドパフォーマンス顧慮
典型 NO NO -
synchronized NO YES NO
Double Check Lock NO NO -
volatile + DCL NO YES YES
static fianl YES YES YES
Holder NO YES YES
enum YES YES YES

総合的にみると、volatile + DCLHolderがベストかな!

liguofeng29
フリーエンジニア。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away