单例模式
特征:
- 构造器私有
- 拥有一个本类实例通过public方法供外界获取
- 多线程访问下的线程安全问题
构造实例的方式:饿汉模式,懒汉模式,DCL(Double Check Lock)方式,内部静态类方式,枚举方式等
-
饿汉模式
public class Person { private static Person instance = new Person(); private Person() { } public static Person getInstance() { return instance; } }
-
懒汉方式
public class Person { private static Person instance = null; private Person() { } public static Person getInstance() { if (instance == null) { instance = new Person(); } return instance; } }
线程安全版本,方法级锁
public class Person { private static Person instance = null; private Person() { } public static synchronized Person getInstance() { if (instance == null) { instance = new Person(); } return instance; } }
缺点:多线程访问时需要排队,影响性能
-
DCL方式
public class Person { private static volatile Person instance = null; private Person() { } public static Person getInstance() { if (instance == null) { synchronized (Person.class) { if (instance == null) { instance = new Person(); } } } return instance; } }
双重检测的原因:优化初级懒汉模式部分多线程访问时的排队现象,避免单例初始化好之后不必要的加锁现象,只会在单例未初始化多线程访问时进行加锁初始化操作。
new Person() 的字节码分为三步
1.在堆内存开辟内存空间。
2.在堆内存中实例化SingleTon里面的各个参数。
3.把对象指向堆内存空间。如果CPU乱序执行132,则会导致提前拿到对象的线程在对象未完全完成初始化动作就开始使用。
volatile保证new Person的时候不会乱序执行导致DCL失效
-
静态内部类方式
public class Person { private static Person instance = null; private Person() { } public static Person getInstance() { return InnerPerson.innerInstance; } private static class InnerPerson { private static Person innerInstance = new Person(); } }
静态内部类可以保证单例的原因是:外部类Person被加载的时候InnerPerson不会被加载,只有调用getInstance的时候才会初始化静态内部类InnerPerson以及其静态成员变量,这个过程类似饿汉模式,由JVM保证线程安全。
缺点:无法传递参数,比如Context上下文参数
-
枚举方式
public enum Person { INSTANCE; Person(){} }
通过Person.INSTANCE的方式获取单例对象,这种方式还可以保证不被暴力反射破坏单例