概念:单例就是一种对象创建模式,它用于产生一个对象的具体实例,他可以确保系统中一个类只产生一个实例。
在Java中单例有两个好处:
1.对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销。
2.由于new对象操作的次数减少,因而对系统内存的试用频率也会降低这将减轻GC压力,缩短GC停顿时间。
单例的六种写法和各自的特点:
饿汉/懒汉/懒汉线程安全/DCL/静态内部类/枚举
饿汉:
1.有一个private构造函数;2.有一个mHungurySingleton变量是static的,同时他的getHungurySingleton方法也是静态的,说明是向外部提供单例的一种方法。
饿汉的缺点就是无法对mHungurySingleton对象做延时加载。因为instance会初始化很多的数据,所以导致他的static变量会被重复的加载。
优化方式:为了延时加载需要进行优化,优化的目标就是懒汉模式。
懒汉:
首先会把他的静态成员变量设为null,不用做初始化就可以,确保系统启动的时候没有额外的负担,这也是性能优化点之一。
然后在getInstance方法中判断当前单例的instance是否存在,存在的话就返回,不存在就重新创建单例。
这里就实现了懒汉的单例,但是在多线程并发这样的实现是无法保证实例是唯一的,就是说在多线程并发情况下,单例懒汉是完全失效的。导致失效最主要的原因就是getInstance方法不是同步的。
懒汉的缺点就是:在多线程并发下这样的实现是无法保证实例是唯一的
优化就是做一个懒汉线程安全的单例模式。
懒汉线程安全模式有两种:
一种在方法名中设置synchronized关键字,这样就保证了如果出现非线程安全问题,由于多个线程会同时进去getInstance方法,synchronized保证只有一个线程可以进入方法,这样就能让懒汉线程安全了。
同步代码块中实现,不在方法中执行,用LazySafetySingleton.class这个对象来保证线程安全。
懒汉线程安全相对于懒汉模式是安全的,但是他有性能效率上的问题。如何解决?就用DCL,就是双重检查锁机制。
DCL双重检查锁机制
双重检查锁机制就是使用同步块加载的方法。因为他有两次检查instance是否为空,一次在同步代码块之外,一次在同步代码块之内。为什么在同步代码块之内还要检查instance是否为空呢?因为可能会有多个线程一起进入同步块外围,如果不在同步块中进行二次检验的话就会生成多个实例了,那就是懒汉模式的一些缺点。
new DclSingleton这个操作也会存在不安全问题,也会报错,解决办法就是将instance变量设为volatil就可以了,volatil这个关键字是可见性,也就是他能保证线程保存在本地不会有instance副本,而每次都回到内存中去读取。
DCL解决了懒汉的线程安全问题。也有缺点就是JVM的即时编译器中存在指令重排序的优化,把instance的变量设为volatil就可以解决这个问题。但是更好的优化就是静态内部类和枚举。
枚举
枚举最大的特点就是写法简单,线程安全。线程安全是有条件的,如果不写实例方法的话,默认情况下枚举的实例是线程安全的,如果要自己添加变量和实例方法一定要注意线程安全。同时枚举在性能上也有更大的优势。
总结:
饿汉:无法对instance实例进行延时加载
懒汉:多线程并发情况下无法保证实例的唯一性
懒汉线程安全:使用synchronized导致性能缺陷。synchronized锁住了方法的同时,也不会让其他的线程进行数据读取操作,所以说对性能使用是有非常大的缺陷的。
DCL:JVM即时编译器的指令重排序,导致实例不会唯一。 使用volatil关键字就可以解决这个问题。
静态内部类/枚举:延时加载instance变量/线程安全/性能优势。
网友评论