总共5种,第6种对单例做了统一管理,第7对防反序列重复创建做了说明。
- 饿汉模式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){
}
public static Singleton getInstance() {
return instance;
}
}
- 懒汉模式
优点:使用时才实例化,线程安全
缺点:第一次获取时反应较慢因为要实例化,
第一次获取以后,每次获取都会有同步开销。
public class Singleton {
private static Singleton instance;
private Singleton(){
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
-
Double Check Lock
优点:使用才初始化,线程安全;
第一次获取后,不会再有同步开销;
缺点:第一次获取时反应较慢因为要实例化;- 缺陷1:没有添加volatile会出现失效,JDK1.6以下没有volatile。
instance = new Instance() 生成汇编指令,会做以下几件事
(1)给Singleton的实例内存分配
(2)调用Singleton的构造函数,初始化成员字段
(3)将instance指向分配的内存空间(此时instance就不是空)
执行顺序可能是1-2-3,有可能1-3-2;因为jdk1.6之前允许CPU乱序执行。线程A执行1-2后切换线程B执行到3,此时线程A就直接取走instance时,再使用时会出错。 - 缺陷2:高并发时会出现双重检测失效。参见<<java并发编程>>最后
- 缺陷1:没有添加volatile会出现失效,JDK1.6以下没有volatile。
public class Singleton {
//JDK1.6以后建议添加volatile
// private volatile static Singleton instance = null;
private static Singleton instance = null;
private Singleton(){
}
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class){
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
- 静态内部类单例实现
优点:使用时才初始化,线程安全
缺点:第一获取时因为实例化比较慢。
public class Singleton {
protected Singleton() {
}
public static Singleton getInstance() {
return Creator.instance;
}
private static class Creator {
private static final Singleton instance = new Singleton();
}
}
- 枚举实例化
优点:简单方便,线程安全,反序列化安全
缺点:多文件,android不建议使用(后续补)
public enum Singleton {
INSTANCE;
}
- 统一管理单例
模仿SystemServiceRegistry.Java注册服务;本质饿汉模式或者懒汉模式,只是做了统一管理。
优点:统一管理,提供统一接口,降低使用成本、对用户隐藏实现
public class SingletonManager {
public static final String SINGLETON_KEY = "Singleton";
private static final ArrayMap<String, Object> MAP_SINGLES_HOLDER = new ArrayMap<>();
/**程序启动时自动注册*/
static {
MAP_SINGLES_HOLDER.put(SINGLETON_KEY,new Singleton());
}
public static Object getSingleton(String key){
return MAP_SINGLES_HOLDER.get(key);
}
/**用户注册*/
public static void registerSingleton(Class entryClass){
String key = entryClass.getName();
if (MAP_SINGLES_HOLDER.containsKey(key)){
return;
}
entryCreate(entryClass, key);
}
private static synchronized void entryCreate(Class entryClass, String key){
try {
Constructor constructor = entryClass.getConstructor();
Object object = constructor.newInstance();
MAP_SINGLES_HOLDER.put(key, object);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
- 1-4中尽管构造函数私有化,但是反序列化时依旧可以重新生成对象。
枚举没有这个问题。
可以通过钩子函数readResolve()函数来避免。
注意点:- 非内置java内置类型,需要自己实现序列化接口
- 设置成0L不会因为调整内部结构,例如新增、去除某个字段,但没有修改serialVersionUID 引发java.io.InvalidClassException
public class Singleton implements Serializable {
private static final long serialVersionUID = 0L;
private static Singleton instance = new Singleton();
private Singleton(){
}
public static Singleton getInstance() {
return instance;
}
private Object readResolve() throws ObjectStreamException {
return instance;
}
}
网友评论