普通线程安全的单例模式
 描述
即在一个JVM中,一个对象只有一个实例存在
 问题
 指令重排
在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的,但是JVM并不保证这两个操作的先后顺序;
理想顺序是:
	a.分配对象的内存空间;
	b.初始化对象;
	c.设置instance指向刚分配的内存地址;
但经过JVM编译和CPU优化之后有可能是a>c>b;
即指令重排之后,当A线程执行同步代码,并执行到c后,此时B线程执行同步代码的第一次null检测,此时instance!=null,从而返回了一个没有初始化完成的instance对象;
 线程安全优化的单例模式
 描述
- 
利用类加载的过程实现懒加载; 单例模式使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载时,这个类的加载过程是线程互斥的; 当第一次调用getInstance的时候,JVM能够保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样就不用担心1.1问题;同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题; 
- 
使用volatile修饰符 该修饰符阻止了遍历访问前后的指令重排,保证了指令执行顺序; java volatile关键字 
 适用
解决指令重排的问题
 问题
- 思想1不一定完美,若在构造函数中抛出异常,实例将永远得不到创建,会出错;
- 思想2无法防止利用反射来重复构建对象;
 示例
- 思想1
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | class Singleton {
 
 
 private Singleton() {}
 
 private static class SingletonFactory{
 private static final Singleton instance = new Singleton();
 }
 
 public static Singleton getInstance(){
 return SingletonFactory.instance;
 }
 
 public Object readResolve(){
 return getInstance();
 }
 }
 
 | 
- 思想2
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | class Singleton {
 private volatile static Singleton instance = null;
 
 private Singleton() {}
 
 public static Singleton getInstance() {
 if (instance == null) {
 synchronized(this) {
 if (instance == null) {
 instance = new Singleton();
 }
 }
 }
 return instance;
 }
 }
 
 | 
 线程安全高级单例模式
 描述
因为只需要在创建类的时候进行同步,所以只要将创建和getInstance()分开,单独为创建加synchronized关键字
 适用
解决内部类或者volatile关键字遇到的问题1
 示例
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | class Singleton {
 private static Singleton instance = null;
 
 private Singleton() {}
 
 private static synchronized void syncInit() {
 if (instance == null) {
 instance = new Singleton();
 }
 }
 
 public static Singleton getInstance(){
 if (instance == null) {
 syncInit();
 }
 return instance;
 }
 
 public Object readResolve(){
 return instance;
 }
 }
 
 | 
 反射问题
 利用反射打破单例
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | Constructor con = Singleton.class.getDeclaredConstructor();
 
 con.setAccessible(true);
 
 Singleton singleton1 = (Singleton)con.newInstance();
 Singleton singleton2 = (Singleton)con.newInstance();
 
 System.out.println(singleton1.equals(singleton2));
 
 | 
 解决,利用枚举
	优点:利用枚举实现的单例模式不仅能够防止反射构造对象,还能保证线程安全;
	缺点:它并非使用懒加载,其实例对象是在枚举类被加载的时候进行初始化的;
| 12
 3
 
 | public enum SingletonEnum {INSTANCE;
 }
 
 |