1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
public class Singleton { private static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
public class Singleton2 { private static Singleton2 instance = new Singleton2();
private Singleton2() {}
public static Singleton2 getInstance() { if (instance == null) { instance = new Singleton2(); }
return instance; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
public class Singleton3 { private static Singleton3 instance = null;
private Singleton3() {}
public static synchronized Singleton3 getInstance() { if (instance == null) { instance = new Singleton3(); }
return instance; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
public class Singleton4 { private static Singleton4 instance;
static { instance = new Singleton4(); }
private Singleton4() {}
public static Singleton4 getInstance() { return instance; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
public class Singleton5 {
private class SingletonHolder { private static final Singleton5 INSTANCE = new Singleton5(); }
private Singleton5() {}
public static Singleton5 getInstance() { return SingletonHolder.INSTANCE; } }
|
1 2 3 4 5 6 7 8 9 10 11 12
|
public enum Singleton6 { INSTANCE
public void whateverMethod() { } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
public class Singleton7 { private volatile static Singleton7 instance = null;
private Singleton7() {}
public static Singleton7 getInstance() { if (instance == null) { synchronized (Singleton7.class) { if (instance == null) { instance = new Singleton7(); } } }
return instance; } }
|
小结
推荐:静态内部类、枚举、双重校验锁
最好的单例模式:枚举
枚举
使用枚举实现单例模式是最好的方法,因为
写法简单
1 2 3 4 5 6 7
| public enum Singleton6 { INSTANCE
public void whateverMethod() { } }
|
枚举实例创建是线程安全的
当一个Java类第一次被真正使用到的时候,静态资源被初始化。Java类的加载和初始化过程都是线程安全的。enum类型会被编译器编译成class T extends Enum
的类。所以,创建一个enum类型是线程安全的。
枚举自己处理序列化
普通的Java类的反序列化过程中,会通过反射调用类的默认构造函数来初始化对象。所以,即使单例中构造函数是私有的,也会被反射给破坏掉。由于反序列化后的对象是重新new出来的,所以这就破坏了单例。但是,枚举的反序列化并不是通过反射实现的。所以,也就不会发生由于反序列化导致的单例破坏问题。
关于枚举类的序列化和反序列化:在序列化的时候Java仅仅是将枚举对象的属性输出到结果中,反序列化的时候则是通过java.lang.Enum
的valueOf
方法来根据名字查找枚举对象。同时,编译器不允许对这种序列化机制进行定制,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。
双重校验锁
volatile 关键字的含义:
- 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,新值对其他线程是立即可见的
- 禁止进行指令重排序
volatile 关键字在本实现的主要作用:禁止进行指令重排序。因为类初始化分两步:
volatile 的不足:无法保证原子性,所以要结合synchronized实现
volatile 的使用条件:
- 对变量的写操作不依赖于当前值
- 该变量没有包含在具有其他变量的不变式中 (a <= b)
静态内部类
优点:延迟加载、线程安全、访问成本低
延迟加载原理:类级内部类只有在第一次被使用的时候才被会装载。
当getInstance
方法第一次被调用的时候,它第一次读取SingletonHolder.instance
,内部类SingletonHolder
类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton
的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次。
线程安全:由虚拟机来保证它的线程安全性。
访问成本低:getInstance
方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。
Prev: Python练习
Next: mac shadowsocks