2014-10-28 81 views
6

在约http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html底部,它说:为什么在双重检查锁定中不可变对象是安全的?

双检锁不可变对象

如果Helper是不可变对象,使得所有助手的字段是决赛,然后双击 - 检查锁定将无需使用易失性字段。这个想法是,对一个不可变对象(比如一个String或一个Integer)的引用应该和int或者float类似。读取和写入对不可变对象的引用是原子的。

样品和一个可变的解释如下:

// Broken multithreaded version 
// "Double-Checked Locking" idiom 
class Foo { 
    private Helper helper = null; 
    public Helper getHelper() { 
    if (helper == null) 
     synchronized(this) { 
     if (helper == null) 
      helper = new Helper(); 
     }  
    return helper; 
    } 
    // other functions and members... 
    } 

第一个原因,它不工作

最明显的原因,它不工作它,初始化助手对象和写入助手字段的写入操作可以完成或不按顺序感知。因此,调用getHelper()的线程可以看到对助手对象的非空引用,但请参阅助手对象的字段的默认值,而不是在构造函数中设置的值。

如果编译器内联调用构造函数,那么如果编译器可以证明构造函数不能抛出异常或执行同步,则初始化对象和写入辅助对象字段的写入操作可以自由重新排序。

即使编译器没有对这些写入重新排序,在多处理器上,处理器或内存系统可能会重新排列这些写入,如在另一个处理器上运行的线程所感知的那样。

我的问题是:为什么不可变的类没有问题?我无法看到重新排序与班级是否可变的任何关系。

谢谢

回答

0

不可变类确实有问题。你引用的部分是在JSR133中对Java内存进行了更改。

具体而言,影响不可变对象的更改与对final关键字所做的某些更改有关。结帐http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#finalRight

最重要的部分是:

为对象的最终字段的值在构造函数中设置。假设对象构造“正确”,一旦构造对象,分配给构造器中最终字段的值将对所有其他线程都可见而不同步。

1

之所以代码是“破”了通常的对象是helper可能是非零而是指向尚未对象在你的报价解释完全初始化尚未。

但是,如果Helper类是不可变的,这意味着所有的字段都最终,the Java Memory Model保证他们安全地发布即使提供对象通过数据种族(这是你的榜样的情况下):

final字段还允许程序员在没有同步的情况下实现线程安全的不可变对象。 线程安全的不可变对象被所有线程视为不可变,即使使用数据竞争将线程的引用传递给线程间的不可变对象。这可以提供安全保证,防止错误或恶意代码滥用不可变类。必须正确使用final字段才能提供不变性保证。

+0

我认为你的术语“完全初始化”意味着与你的链接中的东西有所不同? “一个对象在其构造函数完成时被认为是完全初始化的,一个线程只能在该对象被完全初始化后才能看到对象的引用,保证能够看到该对象最终字段的正确初始化值。 – 2014-10-29 01:56:55

+0

不,它是相同的:在调用'new Helper()'之后,JMM保证final字段已经被初始化,但没有说可能或未被初始化的常规字段。 – assylias 2014-10-29 07:03:23

相关问题