前言: java开发中,设计模式是必须要懂得的知识点,其中单例模式也是面试必考点。此文章仅代表鄙人的总结和理解,如有错漏,欢迎指正...
更全面详细的设计模式,可参考以下链接进行全面性的学习,请点击:软件设计模式概述
一、单例模式实现方式
java开发中,常用的单例模式有4种实现方式,分别是:
1、懒汉式单例模式(线程不安全,通过一定的加锁方式可以实现线程安全)
2、饿汉式单例模式(线程安全的)
3、静态内部类或静态代码块实现的单例模式(线程安全的)
4、枚举方式实现的单例模式(线程安全的)
手写单例三步骤:
1.单例常量private
2.写一个private的构造方法
3.写一个public的getInstance()方法
话不多说,直接上图让我娓娓道来。。。




二、线程安全的单例模式
线程安全的懒汉式,一次加锁一次不加锁
第一次校验,不加锁,增加效率,如果没有单例对象,那么就进行下一步加锁的创建单例
虽然下面的,双重校验锁看似完美,但是一定不要忘了加上volatile关键字,因为初始化对象有三步,要保证顺序创建

线程安全的静态内部类单例模式

静态内部类的优点是:
外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故而不占内存。 即当MySingleton类第一次被加载时,并不需要去加载MysingletonHandler,只有当getInstance()方法第一次被调用时,才会去初始化INSTANCE,第一次调用getInstance()方法会导致虚拟机加载MysingletonHandler类,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
原因:
虚拟机会保证一个类的()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕。
如果在一个类的()方法中有耗时很长的操作,就可能造成多个进程阻塞(需要注意的是,其他线程虽然会被阻塞,但如果执行()方法后,其他线程唤醒之后不会再次进入()方法。同一个加载器下,一个类型只会初始化一次。),在实际应用中,这种阻塞往往是很隐蔽的。
故而,可以看出INSTANCE在创建过程中是线程安全的,所以说静态内部类形式的单例可保证线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
线程安全的static代码块单例模式

单例模式中的内部原理及反射破坏


单例模式-使用反射攻击原理及解决方案
原理:
单例模式分很多种,一种是懒汉(调用getInstance方法才进行生成对象),一种是匿名类(直接在类中就已经初始化好了静态单例对象),一种是饿汉(也是在类中就已经初始化好了静态单例对象),通过反射,可以设置类中构造器的使用权限,拿到构造器直接就可以getInstance()新建一个类,那么就能够直接破坏单例拿到另一个单例。
解决方案:
如果单例是直接在类中已经初始化好的,那么就在其构造器中,定义如果单例为null就抛出异常,那么就不会再执行下一步了。很好的遏制了单例的破坏
(注意:这仅限于单例是直接在类中一new就new出来的哪种;对于另外一种懒汉式的有需要才给单例的,这个解决方案不行。因为反射仍然可以获取构造器权限,且能修改各种控制变量,在构造器中无法简单通过判断单例是否为null就抛出异常)

懒汉式通过简单的抛出异常不可取的原因是因为:
当多线程的时候就要考虑到先后顺序,那么就会不能百分百的阻止通过反射破坏单例,所以懒汉式是线程不安全的,解决方式就是通过加锁来保证实例化时多线程安全
synchronized讲解:同步代码块和同步方法
同步方法就是放在非静态方法的前面即可
同步代码块就是:
synchronized (Singleton.class) {
if (singleton == null){
singleton = new Singleton();
}
}
Singleton.class这里面就是放入需要控制加锁同步的对象,这种方式在一定场景来说更加的节省系统资源,像同步方法上使用的话,一定条件下会更加浪费系统资源
破环单例模式的三种方式:反射,序列化,克隆
懒汉式中防止被破坏的方式是:使用全局变量开关,但是还是不完美,因为反射包中的filed类可以把全局变量进行设置更改全局变量开关


提倡使用枚举的单例模式
枚举防止反射,克隆及序列化破环单例模式的原理
1.enum是不被允许重写clone(),因为Enum类已经将clone()方法定义为final了,并且Enum在使用clone()时直接抛出异常,所以不能被克隆破坏
2.在获取到类构造器后通过newInstance()来实例化前,枚举会报错,所以不能反射破坏
3.枚举低层还是使用readResolve()方法防止序列化对单例进行破坏

ok , 狐狸与小兔的本节技术分享就到这,希望学习的小伙伴能学习到东西。。。
结语:以往都是看别人的博客进行学习技术,其中不乏有精华博客也有吊儿郎当的CV大法文章,所以决定将自己所学所用所整理的知识分享给大家,主要还是想为了后浪们少走些弯路,多些正能量的博客,如有错漏,欢迎指正,仅希望大家能在我的博客中学到知识,解决到问题,那么就足够了。谢谢大家!(转载请注明原文出处)