单例模式
一个类只有一个实例,并且提供一个访问该实例的全局访问点。
主要实现方式:
懒汉式
延时加载、资源利用率高、调用效率低
public class Demo{
//用的时候创建
private static Demo instance;
public static synchronized Demo getInstance(){
if(instance==null)
instance=new Demo();
return instance;
}
}
饿汉式
线程安全、调用效率高,不能延时加载。
public class Demo{
//类初始化时立即加载,创建对象和初始化属性在多线程下是线程安全的
private static Demo instance=new Demo();
private Demo(){}
//方法没有同步,调用效率高
private static Demo getInstance(){
return instance;
}
}
双重检测锁
同步锁放入if中,提高执行效率,因为只有第一次调用对象才需要同步,但由于编译器优化和jvm底层内存模型原因,偶尔出错。不建议使用。
static volatile Instance instance = null;
if(instance==null){
synchronized(Demo.class){
if(instance==null){
instance=new Instance();
}
}
}
instance = new Instance();分为下面三步:
memory = allocate();//1、分配对象内存空间
instance(memory);//2、初始化对象
instance = memory;//3、设置instance指向刚分配内存地址,此刻instance != null
因为2和3不存在数据依赖,可能重排,顺序为1,3,2,导致有些线程取到空值
volatile保证指令不重排
静态内部类实现方式
懒加载,只有在调用函数时才加载对象,加载类是线程安全的,static保证instance是单例的,兼备并发性高和延迟加载。
静态内部类:外部类被装载时,内部类不会被装载,只有被使用时被装载
public class Demo{
private static class DemoInstance{
private static final Demo instance = new Demo();
}
private Demo(){}
public static Demo getInstance(){
return DemoInstance.instance;
}
}
使用枚举实现单例模式
没有懒加载,避免反射创建对象,效率高
public enum Demo{
//枚举元素本身就是单例
INSTANCE;
//定义方法
}
问题
通过反射、序列化与反序列化可以破解上面几种方法(不包括枚举)
setAccess(true);跳过权限检查之后就可以利用反射创建对象
经过序列化和反序列化可以创建新对象