编辑: 思念那么浓 2012-12-25

private Singleton(){} public static Singleton getInstance(){ //第一次校验 if(singleton == null){ synchronized(Singleton.class){ //第二次检验 if(singleton == null){ //创建对象,非原子操作 singleton = new Singleton();

} }

46 | Spring MVC + MyBatis 快速开发与项目实战 } return singleton;

} } 双重校验锁会出现指令重排的问题,所谓指令重排是指JVM为了优化指令,提高程序运行 效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度.singleton = new Singleton()看似原子操作,其实不然,singleton = new Singleton()实际上可以抽象为下面几条 JVM 指令: //1:分配对象的内存空间 memory = allocate();

//2:初始化对象 ctorInstance(memory);

//3:设置 instance 指向刚分配的内存地址 singleton = memory;

上面操作

2 依赖于操作 1,但是操作

3 并不依赖于操作 2,所以 JVM 是可以针对它们进行 指令的优化重排序的,经过重排序后如下: //1:分配对象的内存空间 memory = allocate();

//3:instance 指向刚分配的内存地址,此时对象还未初始化 singleton = memory;

//2:初始化对象 ctorInstance(memory);

可以看到,指令重排之后,singleton 指向分配好的内存放在了前面,而这段内存的初始化 被排在了后面.在线程 A 执行这段赋值语句,在初始化分配对象之前就已经将其赋值给 singleton 引用,恰好 B 线程进入方法判断 singleton 引用不为 null,然后就将其返回使用,导致 程序出错. 为了解决指令重排的问题,可以使用 volatile 关键字修饰 singleton 字段.volatile 关键字的 一个语义就是禁止指令的重排序优化,阻止 JVM 对其相关代码进行指令重排,这样就能够按照 既定的顺序指令执行.修改后的代码如下: /** * 描述:双重校验锁(volatile 解决指令重排问题) * @author Ay * @create 2018/04/14 */ class Singleton{ 第3章Spring 快速上手 |

47 private static volatile Singleton singleton = null;

private Singleton(){} public static Singleton getInstance(){ if(singleton == null){ synchronized(Singleton.class){ if(singleton == null){ singleton = new Singleton();

} } } return singleton;

} } 除了双重校验锁的写法外,比较推荐读者使用最后一种单例模式的写法:静态内部类的单 例模式,具体代码如下: /** * 描述:静态内部类单例模式(推荐的写法) * @author Ay * @create 2018/04/14 */ class ___Singleton{ //2:私有的静态内部类,类加载器负责加锁 private static class SingletonHolder{ private static ___Singleton singleton = new ___Singleton();

} //1:私有化构造方法 private ___Singleton(){} //3:自行对外提供实例 public static ___Singleton getInstance(){ return SingletonHolder.singleton;

} } 当第一次访问类中的静态字段时,会触发类加载,并且同一个类只加载一次.静态内部类 也是如此,类加载过程由类加载器负责加锁,从而保证线程安全.这种写法相对于双重检验锁 的写法,更加简洁明了,也更加不会出错.

48 | Spring MVC + MyBatis 快速开发与项目实战 3.1.3 Spring 单例模式源码解析 回顾完单例模式的内容,来看一下 Spring 的BeanFactory 工厂如何实现单例模式.Spring 的依赖注入(包括 lazy-init 方式)都是发生在 AbstractBeanFactory 的getBean 方法里.getBean 方法内部调用 doGetBean 方法,doGetBean 方法调用 getSingleton 方法进行 bean 的创建.非lazy-init 方式,在容器初始化时进行调用,lazy-init 方式,在用户向容器第一次索要 bean 时进行 调用.AbstractBeanFactory 类源码具体如下: //省略代码 @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false);

下载(注:源文件不在本站服务器,都将跳转到源网站下载)
备用下载
发帖评论
相关话题
发布一个新话题