双重锁定时,为什么Immutable Objects是安全的?

时间:2014-10-28 15:15:28

标签: java thread-safety immutability double-checked-locking

http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html的大约底部,它说:

  
    

双重锁定不可变对象

         

如果Helper是一个不可变对象,Helper的所有字段都是final,那么双重检查锁定将无需使用volatile字段即可。我们的想法是对不可变对象(如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...
  }
  
    

它没有工作的第一个原因

         

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

         

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

         

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

  

我的问题是:为什么不可变课程没有问题?我看不出重新排序与该类是否可变的任何关系。

由于

2 个答案:

答案 0 :(得分:1)

为常规对象“破解”代码的原因是helper可能为非null但指向尚未完全初始化的对象,如引用中所述

但是,如果Helper类是不可变的,意味着它的所有字段都是final,the Java Memory Model保证它们被安全发布,即使该对象通过数据竞争可用(在您的示例中就是这种情况) :

  

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

答案 1 :(得分:0)

不可变类确实存在问题。在JSR133中对Java内存进行更改之后,您引用的部分为为真。

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

重要的部分是:

  

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