(美少女的)单例设计模式
1、饿汉式写法
饿汉式单例是再类加载的时候就立刻初始化,并且创建单例对象。绝对的线程安全,在线程还没有出现以前就实例化了,不可能存在访问安全问题。
优点:没有加入锁、执行效率比较高,在用户体验上来说,比懒汉好
缺点:类加载的时候就初始化,不管用与不用都站着空间,浪费了内存,有可能创建对象后什么也不做。
spring 中的ioc容器ApplicationContext本身就是典型的饿汉式单例。 饿汉式适用于单例较少的
private static final SingleTon singleTon=new SingleTon(); //这里用关键字final 修饰是因为静态对象要在类加载的时候就new一个对象,如果不用的话 就要在静态代码块赋初值。也就是final的特性。
private SingleTon (){}
public static SingleTon getInstance(){
return singleTon;
}
2、懒汉式写法
private static SingleTon singleTon=null;
private SingleTon(){}
public static SingleTon getInstance(){
if(singleTon==null){
singleTon=new SingleTon()
}
return singleTon;
}
如果给getInstance()方法加上synchronized 后就会变成线程安全的方式。但是,假如有俩个线程,一个调用了这个方法,另一个再次调用这个方法就会有Running 变为monitor出现阻塞。直到第一个线程执行玩,第二个才恢复Running 状态继续调用getInstance()。这样的话cpu分配压力会增大,导致大量线程出现阻塞,从而导致程序运行性能大大减少。
3、双重检查锁的单例模式
是解决2的方式这种导致的问题
private static SingleTon singleTon=null;
private SingleTon(SingleTon.class){}
public static SingleTon getInstance(){
if(singleTon==null){
synchroized(){
singleTon=new SingleTon()
//1、分配内存给这个对象
// 2、初始化对象
// 3、将初始化好的对象和内存地址建立关联,赋值
//4、用户初次访问
cpu执行指令的时候会转化成jvm指令执行,会导致上述步骤重新排列。避免这个要在 private static SingleTon singleTon=null; 加上关键字volatile。即private volatile static SingleTon singleTon=null;
}
}
return singleTon;
}
4、内部类的方式创建
这种形式兼顾了饿汉式的内存浪费,也兼顾了synchronrized性能问题。内部类一定要在方法调用之前初始化。内部类只有在调用的时候才会初始化。
private singleTon(){}
public static Singleton getInstance(){
return InnerSingleTon.SingleTon;
}
private static class InnerSingleTon(){
private static final SingleTon singleTon=new singleTon();
}
5、避免反射机制破坏单例
private singleTon(){
if(InnerSingleTon.SingleTon !=null){
throw new RuntimeException("不允许创建多个实例");//如果直接用4的方法,反射创建对象时 会有多个实例。加上这些 如果有反射创建会抛异常。
}
}
public static Singleton getInstance(){
return InnerSingleTon.SingleTon;
}
private static class InnerSingleTon(){
private static final SingleTon singleTon=new singleTon();
}
6、避免序列化破坏单例
public final static SeriableSingle INSTANCE=new SeriableSingle();
private SeriableSingle(){}
public static SeriableSingle getInstance(){
return INSTANCE;
}
private Object readResolve(){ //重写这个方法,覆盖掉原来的。如果没有这个方法 在序列化的时候相当于创建了又一个新得对象,这个方法只是新创建的对象没有被返回
return INSTANCE;
}
7、注册式单例
推荐使用枚举:
public enum EnumSingleTon {
INSTANECE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleTon getInstance(){
return INSTANECE;
}
}