Java
singleton

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


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がベストかな!