1.每种单例写法的优缺点:
饿汉式单例:在类加载的时候就立即初始化,并且创建对象。绝对线程安全,在线程还没出现以前就是实例化了,不可能存在访问安全问题。
优点:没有加任何锁,执行效率比较高,在用户体验上比懒汉式更好。缺点:类加载
的时候初始化,不管用不用得着都要占用内存,适合整体项目单例对象比较少的情况。
懒汉式单例:被外部类调用的时候内部类才会加载。懒汉式单例存在线程安全问题,所以在方法上加锁进行解决,但是在线程数量较多的情况下,如果CPU分配压力上升,会导致大批量线程出现阻塞,从而导致程序运行性能大幅度下降,所以采用了双重检验
锁的单例模式,此时阻塞并不是基于整个类的阻塞,而是在getInstance()
方法内部阻塞,但是只要是用到了synchronnized关键字,总归要上锁,对
程序性能存在一定影响的。所以再一次进行优化,从初始化角度考虑,采用
静态内部类的方式,内部类一定是在方法调用前才会初始化。
注册式单例:又称为登记式单例,就是将每一个实例都登记到某个地方,使用
唯一的标识获取实例。注册式单例有两种写法:一种为容器缓存,一种为枚举
登记。 枚举式单例:在静态代码块中就给Instance进行了赋值,是饿汉式
单例的实现。在JDK枚举的语法特殊性以及反射也为枚举保驾护航,枚举式单
例成为一种比较优雅的实现。 容器式单例:用于创建实例非常多的情况,
便于管理。,但是是非线性安全的。
ThreadLocal线程单例:不能保证其创建的对象是全局唯一,但是能保证在
单个线程中是唯一的,天生的线程安全。ThreadLocal将所有的
对象全部放在了ThreadlocalMap中,为每个线程都提供了一个
对象,实际上是以空间换时间来实现线程间隔离的。
2.破坏单例模式的方式有哪些:
利用反射破坏单例:使用反射调用其构造方法,然后再调用getInstance()方法,
应该就会有两个不同的实例。因为要调用构造方法,会先加载内部类,所以在内部上上加限制以防止反射破坏单例。
序列化破坏单例:当我们将一个单例对象创建好,有时候需要将对象序列化然后写入到磁盘,下次使用时再从磁盘中读取到对象,反序列化转化为内存对象。反序列化后
的对象会重新分配内存,即重新创建。如果序列化的目标的对象为单例,则会
破坏单例。只需要在相关的单例类中增加readResolve方法即可。但是通过
观察还是发现对象实例化了多次。随着创建对象的动作越来越大,内存分配
开销也就随之增大。
3.内部类的执行逻辑: