单例的介绍:
- 单例的概念:
单例模式是一种对象创建模式,它用于产生一个对象的具体实例,它可以确保系统中一个类只产生一个实例。 - 好处:
1、对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销。
2、由于new操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻GC压力,缩短GC停顿时间。
单例的六种写法和各自特点:
- 饿汉:
代码:
缺点:无法对instance实例做延时加载。
优化:采用懒汉。 - 懒汉:
代码:
但是!!它是线程不安全的,下面做实验:
缺点:在多线程并发下这样的实现是无法保证实例是唯一的。
优化:用懒汉线程安全方式。 - 懒汉线程安全:
它有两种方式,如下:
缺点:性能
优化:DCL - DCL(Double-checked Locking双重检查锁):
但是!!代码还是存在问题,因为这块代码不是原子操作,如下:
不是原子操作就意味着JVM针对标红的这句话会有如下处理:
1、给instance分配内存。
2、调用DCLSingleTon构造方法初始化初始化变量。
3、将instance这个对象指向JVM分配的内存空间,
但是!!!JVM有这样一个缺点:在既时编译器中,存在指令重排序的优化,也就是以上说的这三步操作不会按照咱们预期的顺序来执行,有可能第三步在第二步之前,也有可能第二步在第一步之前,这样就会造成线程不安全报错,解决方法如下:说到volatile关键字的作用可能都会想到可见性,也就是保证线程在本地不会存有instance的副本,而每次都会到内存中读取,事实上这种说法是不全面的,最主要使用它的原因就是禁止JVM指令的重排序优化。
缺点:JVM的既时编译器中存在指令重排序的优化。
优化:静态内部类 / 枚举 - 静态内部类【推荐】:
为什么会有这种写法,其这是JVM提供的一种同步控制,什么意思?对于static和final关键字都是线程安全的,所以看一下具体写法:
其中如果getInstance()方法木有调用其类也不会被加载,也就是既可以起到懒汉的效果,又能保证同步安全,而且木有使用synchronized关键字也不会影响性能,另外StaticInnerHolder是私有的也不能被外部所调用,简直完美!
优点:JVM本身机制保证了线程安全 / 没有性能缺陷。 - 枚举:
具体写法如下:
貌似也太简单了吧,但是!!对于里面的实例方法需要保证线程安全。
安全:写法简单 / 线程安全【如果自己添加的实例方法和实例变量需要自己控制线程安全】。
总结:
- 饿汉:无法对instance实例进行延迟加载。
- 懒汉:多线程并发情况下无法保证实例的唯一性。
- 懒汉线程安全:使用synchronized导致性能缺陷。
- DCL:JVM既时编译器的指令重排序。
- 静态内部类 / 枚举:延迟加载 / 线程安全 / 性能优势。
android中的单例:
Application这个类就是典型单例的场景,也是在实际中大量使用的,如下: