线程安全的可用单例模式的几种实现
1.通过static final 实现的饿汉单例
只要编译器一看到该类就会初始化单例,无法达到 延迟加载的目的。
class Singleton{
private static final Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
2.借助静态内部类、static实现的懒汉单例
外部类的静态成员类与外部类没有绑定关系,只有被调用才会加载,从而实现了懒加载。
class Singleton{
private static class SingletonHolder {
private static final Singleton SINGLETON = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.SINGLETON;
}
}
3.双重校验单例模式
加入volatile防止指令重排导致的未完全实例的异常
class Singleton{
private volatile static Singleton singleton;
public static Singleton getInstance(){
if(singleton == null){
synchronized(Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
4.枚举单例
把需要单例功能直接定义到一个单枚举中,此方法是《effectJava》推荐的方法
可以做到懒加载,并防止反射(序列化)、反序列化破坏单例
public enum Singleton{
SINGLETON;
}
注意的地方
出了内部类单例和枚举单例外的几种没有对反射(序列化会通过反射调用无参数的构造方法创建一个新的对象。)、反序列化 进行防护,需要在类的内部加入两种防护
// 序列化会通过反射调用无参数的构造方法创建一个新的对象。
Singleton(){// 这里防止反射破坏单例
if(singleton !=null) {
throw new RuntimeException("cannot create another instance!");
}
}
//防止反序列化获取多个对象的漏洞
//无论是实现Serializable接口,或是Externalizable接口,当从I/O流中读取对象时,readResolve()方法都会被调用到。
private Object readResolve(){
return singleton;
}
问题:
-
- 枚举是什么时候加载的,枚举单例有没有懒加载?
- 枚举是调用的时候加载的,符合懒加载
-
- 序列化和反序列化为什么都要防护,如果防护了序列化就不存反序列化的问题了?
- 多重防护,意义不是特别大(这里不是很确定)
-
- 序列化是否必须要继承序列化接口,不继承可以序列化么。
- 不继承序列化也可以序列化。例如在一些监控或者框架里动态修改字节码。
AOP的无接口类代理就是动态修改字节码。
网友评论