单例

public class SingleTonTest {
     //单例全解
     
     //第一种,懒汉模式(非线程安全的写法)
     
     private SingleTonTest(){};                      //私有化构造函数,因为不能随便让其new对象,故采用private私有化构造函数。
     
     private static SingleTonTest instance = null;   //创建单例对象
     
     public static SingleTonTest getInstance(){      //获取单例对象的方法
           
           if (instance == null){                    //如果单例初始值是null,说明对象还没有被创建,
                                                     //(当instance刚被初始化,此时若有两个线程同时访问,instance还是空的,两个线程会同时调用getInstance方法,
                                                     //此时,两个线程都会判断通过,开始执行new,就会创建两个对象。)
                
                instance = new SingleTonTest();      //则创建对象
                
           }                                                          
           return instance;                          //并返回instance对象返回
           
     }
}
 
public class SingleTonTest {
     // 第二种。(线程安全的)
     private SingleTonTest() {};                               // 私有化构造函数,因为不能随便让其new对象,故采用private私有化构造函数。
 
     private static SingleTonTest instance = null;             // 创建单例对象
 
     public static SingleTonTest getInstance() {               // 获取单例对象的方法
 
           if (instance == null) {                             // 双重检测机制
                synchronized (SingleTonTest.class) {           // 同步锁。为了防止new SingleTonTest 被多次执行,synchronized,每次只允许一个线程访问对象
                     if (instance == null) {                   // 进入Synchronized 临界区以后,还要再做一次判空。因为当两个线程同时访问的时候,线程A构建完对象,
                                                               // 线程B也已经通过了最初的判空验证,不做第二次判空的话,线程B还是会再次构建instance对象。
                           instance = new SingleTonTest();     // 则创建对象
                     }
                }
           }
           return instance;                                    // 并返回instance对象返回
 
     }
//此时也不是绝对的线程安全
}
 
     //指令重排是:比如java中简单的一句 instance = new Singleton,会被编译器编译成如下JVM指令:
 
     memory =allocate();                    //1:分配对象的内存空间
     ctorInstance(memory);                  //2:初始化对象
     instance =memory;                      //3:设置instance指向刚分配的内存地址
 
                                            //但是这些指令顺序并非一成不变,有可能会经过JVM和CPU的优化,指令重排成下面的顺序:
 
     memory =allocate();                    //1:分配对象的内存空间
     instance =memory;                      //3:设置instance指向刚分配的内存地址
     ctorInstance(memory);                  //2:初始化对象
 
                                            //当线程A执行完1,3,时,instance对象还未完成初始化,但已经不再指向null。此时如果线程B抢占到CPU资源,
                                            //执行  if(instance == null)的结果会是false,从而返回一个没有初始化完成的instance对象。
 
 
//如何避免这一情况,我们需要在instance对象前面增加一个修饰符volatile。
 
public class SingleTonTest {
            //第三种
     private SingleTonTest() {} // 私有构造函数
 
     private volatile static SingleTonTest instance = null; // 单例对象
 
     public static SingleTonTest getInstance() {            // 静态工厂方法
           if (instance == null) {
                synchronized (SingleTonTest.class) {
                     if (instance == null) {
                           instance = new SingleTonTest();
                     }
                }
           }
 
           return instance;
 
     }
}
 
//经过volatile的修饰,当线程A执行instance = new Singleton的时候,JVM执行顺序是什么样?始终保证是下面的顺序:
 
memory =allocate();    //1:分配对象的内存空间
ctorInstance(memory);  //2:初始化对象
instance =memory;     //3:设置instance指向刚分配的内存地址
 
//如此在线程B看来,instance对象的引用要么指向null,要么指向一个初始化完毕的Instance,而不会出现某个中间态,保证了安全。
 
 
 
 
 
原文地址:https://www.cnblogs.com/huike/p/8001136.html