单例模式
一、 单线程下单例模式
只有在单线程下才会实现单例模式,一开始还未初始化的时候,一旦并发访问就会发现ts为null,而当时同步并发执行的几个线程就会同时初始化一个新的TestSingle1对象。
二、 最干脆的单例模式
直接将该函数线程同步,保证在第一次初始化的过程中不会出现同时访问该函数的情况,保证了多线程情景下单例模式的实现,但是此函数在多线程的每次访问过程中都会加锁限制访问,实际上是影响了,代码的实现效率,所以不建议采用这种方式。
三、 static修饰下的单例模式
static修饰静态加载,在类加载时就会加载一次,不会进行重复加载,但是会在一开始就对该对象就行直接加载,没有lazy-loading的效果,而且从莫种程度上来说这个也是一种内存资源的浪费。
四、 静态内部类实现的单利模式
静态内部类的优势就在于不会随着TestSingle4类的加载而加载,而是在getSingleInstance()函数条用时才会首次加载,但又因为是static修饰的,所以在首次加载之后就不会重复加载了,所以这种方法算是实现了lazy-loading效果的同时又保证了线程安全。(推荐)
五、 理论上实现的二次校验单例模式
首先,将synchronized修饰转移至函数内部,保证除了首次加载时,不会每次调用的过程中都进行函数加锁;
其次,为什么要进行双重校验呢?因为在首次加载时,如果用并发线程同时访问了getSingleInstance()函数的话就会同时进入加锁模式,如果不进行二次判断的话,加锁是先执行的线程会对ts对象进行初始化,而后执行的线程,如果没有判断的话会再次进行ts对象的初始化,这就违反了我们单例模式的需求;
最后,为什么说这时理论上实现了单例模式呢?
直接看六所展现的代码。
六、 实际上需要的二次校验单例模式
声明ts变量的前面增加了一个volatile关键字,为什么需要添加这个呢?据说是因为java虚拟机在编译的过程中,编译顺序有可能是12-14-16也有可能是12-16-14,而一旦遇到了12-16-14这种编译方式时,因为没有先进行14行ts为空的判断函数就会直接跳至24行返回一个错误的返回值。而在增加了volatile关键字后,java虚拟机在编译的过程中对ts参数就会进行严格的逻辑判断,就只会出现12-14-16这种形式的编译方式,所以程序才能正常执行。
没有想到很好的检验方式,但是在java单例模式相关的介绍中确实都是增加了volatile的关键字的。
七、 不明所以的枚举法单例模式
神一般的写法,首先此类中的只声明了一个枚举对象,这就代表了想要调用此类中的方法,只能通过INSTANCE对象来进行调用,而枚举对象中的参数其实都是final和static修饰的对象所以只会有这么一个实例,而且不会被改变,enum构造方法私有没有办法通过其他方式初始化该对象,以此保证了线程的安全。
代码依然是图片,手敲有益身心健康