日期:2014-05-20  浏览次数:20955 次

java synchronized块的原子性和DCL失效的问题
通常DCL都是这么写的
public class LazySingleton {  
  private static LazySingleton m_instance = null;  
  
  private LazySingleton() {  
  }  
  
  public static LazySingleton getInstance() {  
  if (m_instance == null) {  
  synchronized (LazySingleton.class) {  
  if (m_instance == null) {  
  m_instance = new LazySingleton(); /*网上的分析DCL失效原因时,认为是这里构造函数执行之前,使实例成为非 null而被其他线程所夺取cpu。并读取到没有执行构造函数实例应用*/

  }  
  }  
  }  
  return m_instance;  
  }  
}

在java并发编程实践里面对原子性的描述有2层含义:1.一组语句作为单独的不可分割的单元运行2.一个由同一个锁保护的synchronized块一次只能由一个线程执行
似乎DCL失效时,原子性的第一个含义没做到?是我对synchronized块的原子性认识有错么?

------解决方案--------------------
DCL失效原因是:获得锁的线程正在执行构造函数的时候,其他的线程执行到第一次检查if (m_instance == null)的时候,会返回false,因为已经在执行构造函数了,就不是null,因此,会把没有构造完全的对象返回给线程使用。这是不安全的。
------解决方案--------------------
探讨
3楼的,我是想问这里面的构造函数不是已经包含在了synchronized{}块中了么,为什么不会构造完全(破坏了原子性)?

synchronized(this)
{
i++;
}
i++本来不是原子操作,但套在synchronized块里时就是原子操作了啊

------解决方案--------------------
探讨

引用:
3楼的,我是想问这里面的构造函数不是已经包含在了synchronized{}块中了么,为什么不会构造完全(破坏了原子性)?

synchronized(this)
{
i++;
}
i++本来不是原子操作,但套在synchronized块里时就是原子操作了啊


是正在构造的过程中,比如说:构造这个对象需要1个小时的时间,在开始……

------解决方案--------------------
Java code
Object lock=new Object();
synchronized(lock)
{
  double d = 1.0;
}

------解决方案--------------------
http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html?page=1

class SomeClass {
private Resource resource = null;
public Resource getResource() {
if (resource == null) {
synchronized {
if (resource == null) 
resource = new Resource();
}
}
return resource;
}
}
DCL relies on an unsynchronized use of the resource field. That appears to be harmless, but it is not. To see why, imagine that thread A is inside the synchronized block, executing the statement resource = new Resource(); while thread B is just entering getResource(). Consider the effect on memory of this initialization. Memory for the new Resource object will be allocated; the constructor for Resource will be called, initializing the member fields of the new object; and the field resource of SomeClass will be assigned a reference to the newly created object. 

However, since thread B is not executing inside a synchronized block, it may see these memory operations in a different order than the one thread A executes. It could be the case that B sees these events in the following order (and the compiler is also free to reorder the instructions like this): allocate memory, assign reference to resource, call constructor. Suppose thread B comes along after the memory has been allocated and the resource field is set, but before the constructor is called. It sees that resource is not null, skips the synchronized block, and returns a reference to a partially constructed Resource! Needless to say, the result is neither expected nor desired. 


------解决方案--------------------
JMM 的关系,由于共享变量在本地线程栈中有个复制缓存,可能会因为不能及时更新的关系,而使用得双重检查同步锁同样会产生多个对象的实例。