一、单例模式
单例模式就是保证一个类只能有一个实例对象。
1.实现方式
单例模式有5种常见的实现方式。
(1)饿汉式
饿汉式是在创建类时就进行实例化(类饿的在创建时就带了实例),以后不再改变。饿汉式在类创建的同时就已经创建好一个静态对象,所以天生是线程安全的。
实现的要点有三个:
- 构造私有——避免该类在外部被实例化,也就是说只能自己实例化自己。
- 私有静态成员变量
- 公共静态方法——用来获取静态成员变量
1)破坏饿汉式单例的几种情况
-
反射
通过反射获取类的构造器,然后将私有构造改为公共构造,就能通过构造器创建一个实例了。
【预防反射破坏单例】
在私有构造中加一个if判断,如果实例为空,才允许创建;如果实例不为空就抛出异常。
-
反序列化
如果单例类实现了Serializable接口,那么就可以通过反序列破坏单例。先将单例类序列化成字节流,再反序列化成一个实例,这个实例不通过构造方法创建!
【预防反序列化破坏单例】
在单例类中用readResolve()方法返回我们创建的实例对象,这样再进行反序列化时,就会返回我们创建的实例对象,而不会创建一个新对象。
-
Unsafe
可以通过反射获取unsafe对象,再调用allocateInstance(Singleton.class)来创建新的实例。
【暂无预防措施】
(2)枚举Enum(也属于饿汉式)
java中的枚举类本身也是一种单例模式,而且最重要的是,枚举单例可以防止反射和反序列化破坏单例!
- 创建枚举类Singleton
- 定义枚举变量INSTANCE
(3)懒汉式
懒汉式单例是在第一次实例化时才去创建实例,而不是像饿汉式那样创建类时就创建了实例。
1)多线程不安全问题
在多线程下,懒汉式不能保证单例的唯一性,也就是说可能创建出多个实例。比如有两个线程1、2,线程1先调用了getINSTANCE()方法,判断if条件成立,准备创建实例。但这时,线程1还没有创建出实例,线程2也调用了getINSTANCE(),判断if条件成立,这时线程1和线程2都能创建出一个实例,这样就不是单例了。
2)★解决懒汉式多线程不安全问题的方案
三种解决方案都是对getINSTANCE()进行改造:
-
加synchronized
【缺点】
性能差,只有第一次访问才需要加锁。 -
★双重检查锁定(双检锁DCL)
双检锁是在加synchronized前先判断是否已经有实例了,第一次访问没有实例才去加锁创建实例,其他的再访问有实例了直接返回实例即可。
【为什么要两次判断(为什么要检查两次)?】
如果没有第二个判断条件的话,没创建实例前有两个线程1、2都通过了第一个if判断,线程1拿到了锁,线程2挂起,当线程1创建了实例释放锁之后,线程2会拿到锁,因为没有第二个if判断,所以线程2不管三七二十一还是会创建一个实例,所以要检查两次。
【为什么要加volatile?】
volatile可以避免指令重排。
在new Singleton()时,其实要分三步:①为INSTANCE分配内存空间→②(调用构造方法)初始化INSTANCE→③将INSTANCE指向分配好的内存地址。如果不加volatile,有两个线程1、2,都调用getINSTANCE()时,线程1分配完内存空间直接指向内存地址,此时线程2执行第一个if判断,发现INSTANCEY不为空,线程2就返回实例了,但是这个实例其实还没有被初始化,是不完整的。
【缺点】
性能也差,使用volatile会降低吞吐量。
-
★静态内部类懒汉式
2.饿汉式和懒汉式的区别?
-
从资源加载和性能上来说:
- 饿汉式是在创建类的同时就实例化了一个静态对象,所以不管以后会不会用到,都会占据一定的内存;优点就是第一次调用时速度更快(因为早在创建类时就初始化好了,不用现调现初始化)。
- 懒汉式是调用getINSTANCE()时才会实例化对象,所以第一次调用时需要进行初始化,性能上有些延迟。
-
从线程安全上来说:
- 饿汉式是在创建类时就实例化了一个对象,所以天生就是线程安全的;
-
懒汉式是非线程安全的,为了实现线程安全可以通过加synchronized、双检锁、静态内部类的方式,这三种不同的实现方式又有区别:
- 加synchronized,虽然保证线程安全了,但是每次都要同步执行,影响性能;
- 双检锁通过在getINSTANCE()中进行两次if判断,确保了只有第一次调用时才需要同步,避免了每次都要同步的性能损耗;
- 静态内部类的方式利用类加载器(classloader)的机制来保证初始化实例时只有一个线程,既保证了线程安全,又不影响性能。
3.jdk中有哪些地方体现了单例模式?
(1)Runtime类(饿汉式)
(2)Console类(双检锁)
(3)Collections工具类
Collections工具类中有一些以Empty开头的类,都是懒汉式的单例模式。
二、