zoukankan      html  css  js  c++  java
  • 设计模式—— 七 :单例模式


    @


    单例模式是23个模式中比较简单的模式,应用也非常广泛


    什么是单例模式?

    单例模式的定义:

    Ensure a class has only one instance, and provide a global point of access to it.(确保某一个类 只有一个实例,而且自行实例化并向整个系统提供这个实例。)

    单例设计模式通用类图如下:

    7-1:单例设计模式通用类图

    在这里插入图片描述

    Sngleton类称为单例类,通过使用private的构造函数确保了在一个应用中只产生一个实 例,并且是自行实例化的(在Singleton中自己使用new Singleton())。

    单例模式的通用实例如下:

    public class Singleton {
    	private static final Singleton singleton = new Singleton();
    
    	// 限制产生多个对象
    	private Singleton() {
    	}
    
    	// 通过该方法获得实例对象
    	public static Singleton getSingleton() {
    		return singleton;
    	}
    
    	// 类中其他方法,尽量是static
    	public static void doSomething() {
    	}
    }
    

    单例模式的实现

    单例模式的实现主要有饿汉式、懒汉式、登记式。


    饿汉式

    /**
     * 
     * @description: 饿汉式
     * @author 三分恶
     * @date 2020年3月7日
     */
    public class HungrySingleton {
    	// 创建全局静态变量,保证只有一个实例
        private static volatile HungrySingleton instance  = new HungrySingleton();
    
        //构造函数私有化
        private HungrySingleton() {
            
        }
    
        //获取类的实例
        public static HungrySingleton getInstance() {
            return instance;
        }
    }
    

    懒汉式

    /**
     * 懒汉式单例模式
     * @author 三分恶
     *
     */
    public class LazySingleton {
    	// 创建全局静态变量,保证只有一个实例
        private static LazySingleton instance = null;
    
        // 构造函数私有化
        private LazySingleton() {
        }
    
        public static LazySingleton getInstance() {
            //先判断是否存在当前类的实例
            if (instance == null) {
                instance = new LazySingleton();
            }
            return instance;
        }
    }
    

    线程安全的懒汉式

    懒汉式单例模式在低并发的情况下尚不会出现问题,若系统压力增大,并发量增加时则可能在内存中出现多个实例,破坏了最初的预期。

    可以使用JDK的synchronized同步代码块来解决懒汉式线程安全问题。

    /**
     * @description: 线程安全懒汉式
     * @author 三分恶
     * @date 2020年3月8日
     */
    public class SynchronizedLazySingleton {
    
    	// 创建全局静态变量,保证只有一个实例
    	private static SynchronizedLazySingleton instance = null;
    
    	// 构造函数私有化
    	private SynchronizedLazySingleton() {
    	}
    
    	public static SynchronizedLazySingleton getInstance() {
    		if (instance != null) {
    			return instance;
    		}
    
    		synchronized (SynchronizedLazySingleton.class) {
    			if (instance == null) {
    				instance = new SynchronizedLazySingleton();
    			}
    		}
    		return instance;
    	}
    
    	/*也可以用synchronized关键字修饰getInstance方法来实现
    	 * public static synchronized SynchronizedLazySingleton getInstance() { if
    	 * (instance != null) { return instance; }
    	 * 
    	 * if (instance == null) { instance = new SynchronizedLazySingleton(); } return
    	 * instance; }
    	 */
    }
    

    登记式

    登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。

    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * @description: 登记式,类似类似Spring里面的方法,将类名注册,下次从里面直接获取
     * @author 三分恶
     * @date 2020年3月8日 
     */
    public class RegisterSingleton {
    	private static Map<String, RegisterSingleton> map = new ConcurrentHashMap<>();
    
    	//
        protected RegisterSingleton() {
        }
    
        public static RegisterSingleton getInstance(String name) {
            if (name == null) {
                name = RegisterSingleton.class.getName();
            }
            if (map.get(name) != null) {
                return map.get(name);
            }
            try {
                RegisterSingleton result = (RegisterSingleton)Class.forName(name).newInstance();
                map.put(name, result);
                return result;
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public Map<String, RegisterSingleton> getMap() {
            return map;
        }
    }
    

    单例模式的优缺点


    单例模式的优点

    ● 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地 创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。

    ● 由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要 比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一 个单例对象,然后用永久驻留内存的方式来解决(在Java EE中采用单例模式时需要注意JVM 垃圾回收机制)。

    ● 单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在 内存中,避免对同一个资源文件的同时写操作。

    ● 单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单 例类,负责所有数据表的映射处理。


    单例模式的缺点

    ● 单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途 径可以实现。单例模式为什么不能增加接口呢?因为接口对单例模式是没有任何意义的,它 要求“自行实例化”,并且提供单一实例、接口或抽象类是不可能被实例化的。当然,在特殊 情况下,单例模式可以实现接口、被继承等,需要在系统开发中根据环境判断。

    ● 单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行 测试的,没有接口也不能使用mock的方式虚拟一个对象。

    ● 单例模式与单一职责原则有冲突。一个类应该只实现一个逻辑,而不关心它是否是单例的,是不是要单例取决于环境,单例模式把“要单例”和业务逻辑融合在一个类中。


    单例模式的应用比较广泛,其中最经典的是在Spring中,每个Bean默认是单例的,这样做的优点是Spring容器可以管理这些Bean的生命期,决定什么时候创建 出来,什么时候销毁,销毁的时候要如何处理,等等。如果采用非单例模式(Prototype类型),则Bean初始化后的管理交由J2EE容器,Spring容器不再跟踪管理Bean的生命周期。




    参考:
    【1】:《设计模式之禅》
    【2】:《图解设计模式》
    【3】:《Design-Pattern包教不包会(设计模式包教不包会) 》
    【4】:单例模式(三)
    【5】:设计模式之单例模式(懒汉式单例、饿汉式单例、登记式单例)

  • 相关阅读:
    不可或缺 Windows Native (15)
    不可或缺 Windows Native (14)
    不可或缺 Windows Native (13)
    不可或缺 Windows Native (12)
    不可或缺 Windows Native (11)
    不可或缺 Windows Native (10)
    不可或缺 Windows Native (9)
    不可或缺 Windows Native (8)
    不可或缺 Windows Native (7)
    不可或缺 Windows Native (6)
  • 原文地址:https://www.cnblogs.com/three-fighter/p/12439463.html
Copyright © 2011-2022 走看看