下面是从一个不单例的代码逐步优化成单例的代码的过程。
1、两个类的关系为依赖关系
在一个类A的普通方法中new目标对象,多次执行方法,会创建多个目标对象。
//todo
2、两个类的关系由依赖变关联
扩大目标对象的作用域到普通方法外,在类A的范围内声明一个目标对象,在普通方法中使用目标对象前判断是否已经实例化,如果没有再实例化,否则直接使用。如果有多个类A对象,也会创建多个目标对象。此时目标对象还不是全局单例。
//todo
3、构造函数创建对象
接下来还怎么扩大作用域范围呢?把目标对象声明到哪里,在使用的时候能拿到唯一的实例呢?已经把目标对象的声明由调用方类A的方法内扩大到了它的类内,如果再往其他类C中实例化,在类A中使用,一来不合适,二来new多个类C,同样会有多个目标对象。所以放在调用方类的范围内实例化不能满足需求了。那放在目标对象类本身呢?一个类new不new对象,由它自己说了算。可以,把构造函数变私有,其他类不能去实例化它,只有它自己可以实例化,当不存在实例时再去创建。接下来再提供一个静态公有方法返回它的实例。所有其他类都通过静态公有方法获得目标对象。
//todo
4、多线程单例
多线程时如何保证单例呢?把获取单例对象的静态方法加锁,保证同一时刻只能有一个线程获取实例对象。这样足够了吧?还不行?获取对象会影响性能?还能怎么办?把锁粒度再小一点?
//todo
5、双重锁-懒汉式
锁粒度再小,由锁方法到锁对象?锁方法里的一个分支的代码,锁判断对象是否为创建,未创建则创建的代码。
给Singleton.class加synchronized锁,不太理解是什么意思?
instance属性加上volatile是什么意思?
//todo
以上是双重锁定方式,保证多线程时也单例,称为懒汉式加载
6、饿汉式
下面是饿汉式加载,上来就给静态的Singleton变量实例化,获取实例的方法直接返回此实例。
//todo
在Java中,单例模式的演变过程如下:
1. 1、简单单例模式:
public class Singleton {private static Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;} }
2. 2、考虑多线程,使用同步方法:
public class Singleton {private static Singleton instance;private Singleton() {}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;} }
3. 3、考虑性能,使用同步代码块:
public class Singleton {private static Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();} }}return instance;} }
4. 4、进一步提高性能,使用双检查锁定:
public class Singleton {private volatile static Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) { instance = new Singleton();}}}return instance;} }
5. 5、使用枚举实现单例:
public enum Singleton {INSTANCE;public void doSomething() {...} }
这个是Java中单例模式的演变过程,从简单模式考虑多线程性能问题,最终使用枚举实现一个线程安全的单例。