HyZxx

导航

 

一 定义 

  单例模式(Singleton Pattern):是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。

  隐藏其所有的构造方法

  属于创建性模式

 

二 单例模式的几种实现方式

  (一)饿汉式单例:在单例类首次加载时就创建单例

public class HungrySingleton {

    private static final HungrySingleton hungrySingletonm = new HungrySingleton();

    private HungrySingleton(){}

    public HungrySingleton getInstance(){
        return hungrySingletonm;
    }

}
public class HungryStaticSingleton {

    private static final HungryStaticSingleton hungrySingletonm;

    private HungryStaticSingleton(){}

    static {
        hungrySingletonm = new HungryStaticSingleton();
    }

    public HungryStaticSingleton getInstance(){
        return hungrySingletonm;
    }

}

  上面两种方式没有本质的区别,都是在类加载时就创建类的实例

  优点:执行效率高,性能高,没有任何的锁

  缺点:某种情况下,可能会造成内存浪费

 

  (二)懒汉式单例:被外部调用时才创建实例

public class LazySingleton {
    private static LazySingleton instance;

    private  LazySingleton(){}

    public static LazySingleton getInstance(){
        if(instance == null){
            instance = new LazySingleton();
        }
        return instance;
    }

}

  A方式  优点:不是加载类的时候就被创建,可以节约内存空间

        缺点:不是线程安全的,多线程情况下,会产生多个实例

public class LazySingleton {
    private static LazySingleton instance;

    private  LazySingleton(){}

    public static synchronized LazySingleton getInstance(){
        if(instance == null){
            instance = new LazySingleton();
        }
        return instance;
    }

}

  B方式  优点:节约了内存空间,并且是线程安全的

        缺点:性能很低  

public class LazySingleton {
    private volatile static LazySingleton instance;
    private  LazySingleton(){}

    public static LazySingleton getInstance(){
        if(instance == null){    //第一层检查:检查是否阻塞
            synchronized(LazySingleton.class){
                if(instance == null){  //第二层检查:检查是否要重新创建实例
                    instance = new LazySingleton();    //这有一个指令重排序的问题,上面的变量上加上volatile字段
                }
            }
        }
        return instance;
    }

}

  C方式  双重检查,比较好的一种写法

     优点:性能提高了,线程安全了

     缺点:可读性不好,不够优雅

public class StaticInnerClassSingleton {

private StaticInnerClassSingleton(){
if(Holder.INSTANCE != null){
throw new RuntimeException("不允许非法访问");      //防止反射破坏单例
}
}

public static StaticInnerClassSingleton getInstance(){
return Holder.INSTANCE;
}

private static class Holder{
private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
}
}

  D方式  写法优雅,利用了java本身的语言特性,性能高,避免了内存浪费

上面的几种写法都会被反射和序列化破坏单例

 

  (三)注册式单例:将每一个实例都缓存到统一的容器中,使用唯一标示获取实例  

public enum EnumSingleton {
    INSTANCE;
    
    public static EnumSingleton getInstance(){
        return INSTANCE;
    }
}

枚举式单例可避免序列化破坏和反射破坏

 

public class ContainerSingleton {
    private ContainerSingleton(){}
    private static Map<String,Object> ioc=new ConcurrentHashMap<String,Object>();

    public static Object getBean(String className){
        Object instance = null;
        if(!ioc.containsKey(className)){
            try {
                instance = Class.forName(className).newInstance();
                ioc.put(className,instance);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return instance;
        }else{
            return ioc.get(className);
        }
    }
}

容器式单例适用于创建实例非常多的情况,便于管理。但是,是非线程安全的。

 

  (四)ThreadLocal单例:保证线程内部的全局唯一,且天生线程安全。严格上说不能算作是一种单例模式,只是保证了每个线程里的实例是一个

public class ThreadLocalSingleton {

    private static final ThreadLocal<ThreadLocalSingleton> threadLocalSIngleton =
            new ThreadLocal<ThreadLocalSingleton>(){
                @Override
                protected ThreadLocalSingleton initialValue() {
                    return new ThreadLocalSingleton();
                }
            };

    private ThreadLocalSingleton(){}

    public ThreadLocalSingleton getInstance(){
        return threadLocalSIngleton.get();
    }

}

 

三   学习单例模式的重点

  1. 私有化构造器

  2. 保证线程安全

  3. 延迟加载

  4. 防止序列化和反序列话破坏单例

  5. 防御反射攻击单例

 

posted on 2020-03-07 14:40  HyZxx  阅读(162)  评论(0编辑  收藏  举报