【设计模式】单例模式

单例模式

优点

  • 可以保证内存中只有一个实例,减少了内存的开销
  • 可以避免对资源的多重占用
  • 可以优化和共享资源的访问

缺点

  • 扩展难,违背了开闭原则
  • 不利于调试(并发测试中)
  • 功能设计不合理就会违背单一职责原则

饿汉式单例模式

/**
 * 饿汉式单例:使用静态块机制,在对象类加载的时候就实例化(类还没加载就实例)。
 * 优点:可以保证绝对线程安全,执行效率高
 * 缺点:当系统中有大批量的单例对象存在,可能会造成大量内存的浪费。
 */
class HungrySingleton{
    private static final HungrySingleton hungrySingleton = new HungrySingleton();
    private HungrySingleton(){

    }
    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
}

//第二种写法
class HungrySingleton{
    private static final HungrySingleton hungrySingleton;
    //使用静态块
    static{
        hungrySingleton = new HungrySingleton();
    }
    private HungrySingleton(){

    }
    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
}

懒汉式单例模式

/**
 * 懒汉式单例:被使用时才初始化。
 * 优点:
 * 缺点:
 */
class LazySingleton{
    //volatile防止指令重排序
    private volatile static LazySingleton instance;

    private LazySingleton(){

    }

    public static LazySingleton getInstance(){
        //判断是否要阻塞
        if (instance == null){
            synchronized (LazySingleton.class){
                //判断是否要实例化对象
                if (instance == null){
                    instance = new LazySingleton();
                    //指令重排问题
                }
            }
        }
        return instance;
    }

}

优化单例模式

静态内部类

/**
 * 静态内部类
 * 优点:屏蔽了饿汉式的内存损耗和懒汉式的synchronized性能消耗
 * 缺点:可以通过(反射/反序列化)的方式破坏单例模式
 */
class StaticInnerClassSingleton{
    //使用StaticInnerClassSingleton时,默认先初始化内部类
    //如果没使用,内部类不加载
    private StaticInnerClassSingleton(){

    }
    
	//static使单例模式的空间共享,保证这个方法不会被重写、重载。
    public static StaticInnerClassSingleton getInstance(){
        return LazyHolder.staticInnerClassSingleton;
    }
    
	//java本身语言特点,默认不加载内部类
    private static class LazyHolder{
        private static final StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
    }
}



/**
 * 优化反射破坏后的静态内部类
 * 优点:屏蔽了饿汉式的内存损耗和懒汉式的synchronized性能消耗,不能通过反射的方式进行破坏。
 * 缺点:可以通过反序列化的方式破坏单例,在构造函数中抛出异常,不够完美。
 */
class StaticInnerClassSingleton{
    //使用StaticInnerClassSingleton时,默认先初始化内部类
    //如果没使用,内部类不加载
    private StaticInnerClassSingleton(){
		if(LazyHolder.staticInnerClassSingleton != null){
            throw new RuntimeException("不允许创建多个实例");
        }
    }
    
	//static使单例模式的空间共享,保证这个方法不会被重写、重载。
    public static StaticInnerClassSingleton getInstance(){
        return LazyHolder.staticInnerClassSingleton;
    }
    
	//java本身语言特点,默认不加载内部类
    private static class LazyHolder{
        private static final StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
    }
}



/**
 * 优化(反射/反序列化)破坏后的静态内部类
 * 优点:屏蔽了饿汉式的内存损耗和懒汉式的synchronized性能消耗,不能通过(反射/反序列化)的方式进行破坏。
 * 缺点:在构造函数中抛出异常,不够完美。
 */
class StaticInnerClassSingleton implements Serializable{
    //使用StaticInnerClassSingleton时,默认先初始化内部类
    //如果没使用,内部类不加载
    private StaticInnerClassSingleton(){
		if(LazyHolder.staticInnerClassSingleton != null){
            throw new RuntimeException("不允许创建多个实例");
        }
    }
    
	//static使单例模式的空间共享,保证这个方法不会被重写、重载。
    public static StaticInnerClassSingleton getInstance(){
        return LazyHolder.staticInnerClassSingleton;
    }
    
	//java本身语言特点,默认不加载内部类
    private static class LazyHolder{
        private static final StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
    }
    
    private Object readResolve(){
        return LazyHolder.staticInnerClassSingleton;
    }
}

反射破坏单例

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    Constructor<StaticInnerClassSingleton> constructor = StaticInnerClassSingleton.class.getDeclaredConstructor();
    constructor.setAccessible(true);
    //通过反射创建的实例
    StaticInnerClassSingleton instance1 = constructor.newInstance();
    //正常获取的实例
    StaticInnerClassSingleton instance2 = StaticInnerClassSingleton.getInstance();
    System.out.println(instance1 == instance2);		//false
}


public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
{
	......
                   
	//官方不支持反射创建enum类型的
	if ((clazz.getModifiers() & Modifier.ENUM) != 0)
	throw new IllegalArgumentException("Cannot reflectively create enum objects");
                   
    ......
}

枚举式单例

/**
 * 枚举式单例
 * 优点:屏蔽了饿汉式的内存损耗和懒汉式的synchronized性能消耗,不能通过(反射/反序列化)的方式进行破坏。
 * 缺点:在类加载时将所有的对象初始化放在类内存中,和饿汉式单例差不多,不适合大量创建单例对象。
 */
enum EnumSingleton{
    INSTANCE;
    private Object data;
    public Object getData(){
        return data;
    }
    public void setData(Object data){
        this.data = data;
    }
    public static EnumSingleton getInstance(){
        return INSTANCE;
    }
}

反序列化破坏单例

public static void main(String[] args) throws IOException, ClassNotFoundException {
    
    StaticInnerClassSingleton instance1 = StaticInnerClassSingleton.getInstance();
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("testSerializable"));
    oos.writeObject(instance1);
    oos.close();

    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("testSerializable"));
    StaticInnerClassSingleton o = (StaticInnerClassSingleton) ois.readObject();
    System.out.println(instance == o);
}

容器式单例

/**
 * 容器式单例
 * 优点:适用于大量单例对象的创建,便于管理。
 * 缺点:线程不是安全的。
 */
class ContainerSingleton{
    private ContainerSingleton(){}
    private static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>();
    public static Object getBean(String className){
        synchronized (ioc){
            if (!ioc.containsKey(className)){
                Object obj = null;
                try {
                    obj = Class.forName(className).newInstance();
                    ioc.put(className, obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return obj;
            }else {
                return ioc.get(className);
            }
        }
    }
}

通过clone破坏单例

public static void main(String[] args) throws CloneNotSupportedException {
        StaticInnerClassSingleton singleton = StaticInnerClassSingleton.getInstance();
        StaticInnerClassSingleton clone = singleton.clone();
        System.out.println(singleton == clone);
}



class StaticInnerClassSingleton implements Serializable,Cloneable{
    //使用StaticInnerClassSingleton时,默认先初始化内部类
    //如果没使用,内部类不加载
    private StaticInnerClassSingleton(){
        if(LazyHolder.staticInnerClassSingleton != null){
            throw new RuntimeException("不允许创建多个实例");
        }
    }

    //static使单例模式的空间共享,保证这个方法不会被重写、重载。
    public static StaticInnerClassSingleton getInstance()	{
        return LazyHolder.staticInnerClassSingleton;
    }

    //重写clone方法,放在复制破坏单例。
    @Override
    protected StaticInnerClassSingleton clone() throws CloneNotSupportedException {
        return LazyHolder.staticInnerClassSingleton;
    }

    //java本身语言特点,默认不加载内部类
    private static class LazyHolder{
        private static final StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
    }

    private Object readResolve(){
        return LazyHolder.staticInnerClassSingleton;
    }
}

线程单例实现ThreadLocal

/**
 * 线程单例实现ThreadLocal
 * 优点:在单个线程中是唯一的
 * 缺点:不能保证全局对象唯一
 */
class ThreadLocalSingleton{
    private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance = new ThreadLocal<ThreadLocalSingleton>(){

        @Override
        protected ThreadLocalSingleton initialValue() {
            return new ThreadLocalSingleton();
        }
    };
    private ThreadLocalSingleton(){}
    public static ThreadLocalSingleton getInstance(){
        return threadLocalInstance.get();
    }
}

单例模式的应用

JDK中的Runtime类

Spring中的BeanFactory的getBean()方法

posted @ 2021-04-04 19:06  糯米糍好吃!  阅读(53)  评论(0编辑  收藏  举报