双重校验锁实现单例模式
package day1; /** 特点:懒汉模式,只有使用时,才进行创建 * 首次调用getInstance()才使用synchronized加锁,后续使用时无需加锁 * 该代码存在效率问题,因为即使已经产生了单例之后,之后调用了getInstance()方法之后仍然还会加锁解锁, * 加锁和解锁会影响系统的性能 * ====================> * 于是有了双重校验锁机制 */ //问题一:final的作用:防止子类不适当地重写父类中的方法,破坏单例模式 public final class Singleton { private static Singleton instance = null; private Singleton() {} // 问题二:私有的构造方法,只能类内调用,但是私有不能防止反射来创建新的实例(反射机制很强大) public static Singleton getInstance() { synchronized (Singleton.class) { // 避免多线程并发访问 if (instance == null) { instance = new Singleton(); } return instance; } } // 如果Singleton实现了序列化接口,还要做什么来防止反序列化破坏单例 // 加上以下方法 // 有了这个方法,那么readResolve的结果会当成反序列化字节码生成的对象 public Object readResolve() { return instance; } } final class Singleton_1 { private Singleton_1() { } private static Singleton_1 instance = null; public static Singleton_1 getInstance() { if (instance == null) { // 首次访问会同步,之后的不会进入synchronized方法 synchronized (Singleton_1.class) { if (instance == null) { instance = new Singleton_1(); } } } return instance; } } /** * 但是该方法仍然存在问题 * * if (instance == null) 在synchronized外部,instance静态变量不是有序的,synchronized无法禁止指令重排 * * instance = new Singleton_1(); * * 字节码包含4部分: * * 1. new 新建一个实例 * * 2. 复制这个实例的引用 * * 3. 通过该引用调用构造方法 * * 4. 引用用来进行赋值操作putstatic * * * * JIT即时编译器进行指令重排,将4排到3的前面,那么t1还未完全将构造方法执行完毕,如果在构造方法中要执行很多初始化操作 * * 那么t2拿到的instance类变量是非空的,但是一个未初始化完毕的单例,那么就会出现问题 * * 解决办法,就是对instance使用volatile修饰 */ // 懒汉式-完整版 final class Singleton_2 { private Singleton_2() {} private volatile static Singleton_2 instance = null; public static Singleton_2 getInstance() { // 读屏障,写屏障之后的不能指令重排 if (instance == null) { // 首次访问会同步,之后的不会进入synchronized方法 synchronized (Singleton_2.class) { if (instance == null) { instance = new Singleton_2(); // 写屏障,写屏障之前的不能指令重排 } } } return instance; } } // 饿汉式-完整版 final class Singleton_3 { // 1.私有的构造方法,只能类内调用 private Singleton_3() {} // 2.静态变量的初始化,是在类的加载过程(类加载只有一次)中完成的,不会有多线程并发问题 private static final Singleton_3 instance = new Singleton_3(); // 3.创建一个对象,让外面的类可以拿到对象的引用 // 这里使用方法返回,而不是直接把instance设置成public,的原因有很多: // 比如:使用方法的话,可以增加封装性;并且方法可以支持对泛型的控制,比较灵活 public static Singleton_3 getInstance() { return instance; } } // 枚举类 // 枚举类构建的单例不能被反序列化和反射模式破坏 // instance在类加载阶段完成 // 枚举类的实例只有一个instance enum Singelton { instance; } // 线程安全的实现形式 使用内部类 final class Singleton_4 { private Singleton_4() {} // 内部类 懒汉式,只有调用了getInstance方法,才创建单例 private static class LazyHolder { // 内部类类加载的时候 对静态变量初始化 由JVM保证线程的安全性 static final Singleton_4 instance = new Singleton_4(); } public static Singleton_4 getInstance() { return LazyHolder.instance; } }
作者:Ryanjie
出处:http://www.cnblogs.com/ryanjan/
本文版权归作者和博客园所有,欢迎转载。转载请在留言板处留言给我,且在文章标明原文链接,谢谢!
如果您觉得本篇博文对您有所收获,觉得我还算用心,请点击右下角的 [推荐],谢谢!