4.2.1 单例模式
类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例。Spring的IoC创建Bean对象时,默认的就是单例模式。
优点:防止其他对象对自己进行实例化,确保访问的都是一个实例,这样可以节约内存资源,提高系统性能,避免对共享资源的多重占用。
缺点:不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。单例模式没有抽象层,不能扩展,且职责过重,违背了单一性原则
应用场景:网站计数器、线程池、数据库连接池、任务管理器等等。
-
饿汉式,静态常量(可以使用)
在类装载时完成了实例化,避免了线程同步问题,但是没有懒加载效果,可能会造成内存浪费
public class Singleton { private Singleton() { //构造器私有化 } //内部创建对象 private final static Singleton instance = new Singleton(); //提供静态方法返回对象 public static Singleton getInstance(){ return instance; } }
-
懒汉式:等第一次使用的时候再初始化,即“懒加载”。
-
线程不安全(在单线程环境中适用)
实际开发中不会使用,如果在多线程环境中,一个线程在执行 if 判断成功后还没有开始创建对象时,第二个线程也正好通过了 if 判断,这样就会产生两次实例化。
public class Singleton { private static Singleton instance; private Singleton(){ //构造器私有化 } //在静态方法内创建对象,并返回,调用方法才会创建对象 public static Singleton getInstance(){ if (instance == null) { instance = new Singleton(); } return instance; } }
-
线程安全,同步方法(基本上从并行变成了串行,效率低,不推荐)(加在方法上,每次线程都会进行synchronized等待)
public class Singleton { private static Singleton instance; private Singleton(){ //构造器私有化 } //在静态方法内创建对象,添加synchronized使线程安全 public static synchronized Singleton getInstance(){ if (instance == null) { instance = new Singleton(); } return instance; } }
-
双重检查(推荐使用的)(针对1和2的情况,如果多个线程通过了第一次 if 的判断,但因为synchronized存在,后续等待的线程不会再重复实例化对象。而且,除了初始化的时候会加锁,后续的调用都是直接返回,解决了多余的性能消耗。)
外层的 if 让后续的线程不再进行synchronized等待,而第二层 if 可以阻止多次实例化
其中volatile是Java提供的一种轻量级的同步机制。Java 语言包含两种内在的同步机制:同步块 和 volatile 变量,相比于synchronized(重量级锁),volatile更轻量级,因为它不会引起线程上下文的切换和调度。而在变量上添加volatile,作用相当于一个内存屏障,所以才有了双检锁。
public class Singleton { //持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载(懒加载) private static volatile Singleton instance = null; //私有构造方法,防止被实例化 private Singleton() { //判断对象是否创建,若创建则抛出异常。 if(instance!=null){ throw new RuntimeException(); } } //静态工程方法,创建实例并返回 public static Singleton getInstance() { //synchronized关键字,加锁,可保证线程的安全性 if (instance == null) { synchronized (Singleton.class) { if (instance == null) { //双重检查,防止线程问题 instance = new Singleton(); } } } return instance; } }
-
-
静态内部类(推荐使用)
public class Singleton { private Singleton(){ } //创建静态内部类,JVM只在第一次装载类时初始化,所以保证了线程的安全 private static class SingletonInterior{ private final static Singleton SINGLETON = new Singleton(); } //在静态方法内创建对象,并返回,调用方法时装载SingletonInstance,创建对象 public static synchronized Singleton getInstance(){ return SingletonInterior.SINGLETON; } }
-
枚举(《Effective JAVA》中大为推崇)
可以避免多线程问题,还能防止反射或序列化重新创建新的对象,但相对的牺牲了静态工厂方法的优点,无法实现懒加载(本质上和饿汉模式相同,区别仅在于公有的静态成员变量)
public enum Singletion{ INSTANCE; //一个属性保证了是单例 ....属性方法.... }