单例模式三个主要特点:
1、构造方法私有化;
2、实例化的变量引用私有化;
3、获取实例的方法共有。
为什么使用单例?
1.私有化构造器并不安全,(因为可以借助AccessibleObject.setAccessible方法,通过反射机制调用私有构造器。)
2.序列化问题
懒汉式
当有多个线程并行调用 getInstance() 的时候,就会创建多个实例。也就是说在多线程下不能正常工作;
虽然做到线程安全,并且解决了多实例的问题,但是它并不高效。因为在任何时候只能有一个线程调用 getInstance() 方法;
使用同步块加锁,有两次instance == null 检测(因为会出现有多个线程一起进入同步块外的 if,如果在同步块内不进行二次检验的话就会生成多个实例);
但instance = new Singleton() 这句代码会分成:
①给 instance 分配内存
②调用 Singleton 的构造函数来初始化成员变量
③将instance对象指向分配的内存空间
由于JVM会出现指令重排的现象,可能出现①②③也可能出现①③②,如果是后者的执行顺序,第③步instance就是非空了,所以
要将instance变量声明成volatile(禁止指令重排序优化),可理解成对volatile变量的写操作都先行发生于后面对这个变量的读操作
静态内部类的懒汉式,线程安全。
饿汉式
单例的实例被声明成 static 和 final 变量了,在第一次加载类到内存中时就会初始化,所以创建实例本身是线程安全的
枚举式
可以通过SingletonEnum.INSTANCE来访问实例,①线程安全的,②防止反序列化导致重新创建新的对象
个人推荐使用枚举实现单例!