标签:instance ima com 其他 代码 alt nbsp 同步 改进
有时候需要推迟一些高开销的对象初始化操作,并且只有在使用这些对象时才进行初始化。此时,常用的可能就是延迟初始化,例如:懒汉式单例模式,但是要正确的实现线程安全的延迟初始化需要一些技巧,下面是非线程安全的示例代码:
public class UnsafeLazyInit { private static Instance instance ; public static Instance getInstance(){ if(instance == null ) //1.A线程执行 instance = new Instance() ; //2.B线程执行 return instance ; } }
在示例代码中,假如A线程执行步骤1的同时,B线程执行步骤2,线程A可能会看到instance引用的对象还没有初始化完成。
我们可以对getInstance()方法做同步处理来实现线程安全的延迟初始化。示例代码如下:
public class UnsafeLazyInit { private static Instance instance ; public synchronized static Instance getInstance(){ if(instance == null ) instance = new Instance() ; return instance ; } }
对getInstance()方法加上了synchronized关键字进行同步处理,这将导致线程获取锁和释放锁的开销,并且线程之间竞争锁会造成阻塞。如果getInstance()方法不会被多个线程频繁调用,那么这个方案也能够提供令人满意的性能。如果需要多线程频繁的调用,将会导致线程执行性能下降。
进一步改进,可以使用双重检查锁定来实现延迟初始化。示例代码如下:
public class UnsafeLazyInit { private static Instance instance ; //1 //2 public synchronized static Instance getInstance(){ //3 if(instance == null ){ //4.第一次检查 synchronized(UnsafeLazyInit.class){ //5.加锁 if(instance ==null ) //6.第二次检查 instance = new Instance(); //7.初始化对象: 问题的根源 } } return instance ; } }
如上代码所示,如果第一次检查结果不为null,那么就不需要进行加锁和初始化操作 。因此,可以大幅度降低synchronized带来的性能开销,看起来似乎两全其美:当多个线程试图在同一时间创建一个对象时,第5步代码通过加锁保证了只有一个线程能够创建对象。
在对象创建好之后,执行getInstance()方法将不需要再次获得锁,直接返回创建的对象。
但是以上代码还有一个错误的优化!当线程A执行到第7步时,线程B执行到第4步,这时候线程B读取到的instance可能不为null,但是instance的引用却还没完成初始化。
在第7步创建一个对象,可以拆分为以下三行伪代码执行:
1. memory = allocate() ;//分配对象的内存空间 2. ctorInstance(memory) ;//初始化对象 3. instance = memory ;//引用指向内存空间
上述的伪代码,可能会被重排序,在JMM中,这种重排序是被允许的,它只保证重排序不会改变对单线程的执行结果,上述代码2、3步骤重排序不会影响单线程的执行结果,重排序之后的执行顺序如下:
1. memory = allocate() ;//分配对象的内存空间 3. instance = memory ;//引用指向内存空间 //注意: 还没有初始化 2. ctorInstance(memory) ;//初始化对象
如果是单线程访问,重排序并不会影响最后的执行结果,如下图所示:
下图表示多线程并发执行的情况:
如上图,重排序只能保证线程A能够正确的访问对象,线程B可能访问到一个还没初始化完成的对象。
在知晓了问题的根源之后,要实现线程安全的延迟加载,可以考虑以下两点:
(1)不允许2和3重排序。
(2)允许2和3重排序,但是这个重排序对其他线程不可见。
基于volatile的解决方案:
只需要把以上示例的代码做一点小修改(instance声明为volatile型),就可以实现线程安全的延迟初始化。示例代码如下:
public class UnsafeLazyInit { private volatile static Instance instance ; public synchronized static Instance getInstance(){ if(instance == null ){ synchronized(UnsafeLazyInit.class){ if(instance ==null ) instance = new Instance(); //instance为volatile,会插入内存屏障,禁止重排序 } } return instance ; } }
注意:以上方法需要JDK5或者更高的版本,JDK5之后使用新的内存模型JSR-133内存模型,增强了volatile的内存语义,使volatile和锁拥有相同的内存语义;
标签:instance ima com 其他 代码 alt nbsp 同步 改进
原文地址:http://www.cnblogs.com/dquery/p/7077154.html