不变性是否保证线程安全?

Mac*_*Mac 1 java multithreading immutability thread-safety

好吧,考虑Immutable下面给出的不可变类:

public final class Immutable
{
    final int x;
    final int y;
    public Immutable(int x,int y)
    {
        this.x = x;
        this.y = y;
    }
    //Setters
    public int getX()
    {
        return this.x;
    }
    public int getY()
    {
        return this.y;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,我创建的对象Immutable的一类Sharable,其目的是要由多个线程共享:

public class Sharable
{
    private static Immutable obj;
    public static Immutable getImmutableObject()
    {
        if (obj == null) --->(1)
        {
            synchronized(this)
            {
                if(obj == null)
                {
                    obj = new Immutable(20,30); ---> (2)
                }
            }
        }
        return obj; ---> (3)
    }
}
Run Code Online (Sandbox Code Playgroud)

Thread A看到objas null并移动到synchronized块并创建对象.现在,由于Java内存模型(JMM)允许多个线程在初始化开始之后但在结束之前观察对象.因此,Thread B可以看到写入obj在写入字段之前发生Immutable.因此Thread B可以看到部分构造的Immutable可能处于无效状态并且其状态可能在以后意外地改变.

它不是Immutable非线程安全的吗?


编辑
好了,在查看了很多内容并进行了一些注释之后,我知道在构造对象之后,你可以安全地共享对线程之间的不可变对象的引用.此外,正如@Makoto所提到的,通常需要声明包含其引用volatile的字段以确保可见性.此外,正如@PeterLawrey所述,声明对不可变对象的引用final使得该字段成为thread-safe

Pet*_*rey 7

因此,线程B可以看到在写入不可变字段之前发生的对objas的写入.因此,线程B因此可以看到部分构造的Immutable,其可能处于无效状态并且其状态可能在以后意外地改变.

在Java 1.4中,这是真的.在Java 5.0及更高版本中,最终字段在构造后是线程安全的.

  • @Mac:你似乎要求[这个规范](http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.5) (2认同)

Mak*_*oto 5

你在这里描述的是两件不同的事情.首先,Immutable 是线程安全的,如果操作正在做的一个实例它.

线程安全在某种程度上是确保内存不会被另一个线程意外覆盖.就使用而言Immutable,您永远不会覆盖其实例中包含的任何数据,因此您可以确信,在并发环境中,Immutable当您构造对象时,对象将在线程操作它时相同.

你所拥有的是双重检查锁定的破坏实现.

你是对的,线程A和线程B可能会在实例设置之前对其进行践踏,从而使对象的整体不变性Immutable完全没有实际意义.

我相信解决这个问题的方法是使用volatile你的obj字段的关键字,这样Java(> 1.5)将尊重单例的预期用途,并禁止线程覆盖内容obj.

现在,读得更近了,你有一个不可变的单例,它需要两个静态数据才能存在,这似乎有点不稳定.似乎更像是适合工厂.

public class Sharable {
    private Sharable() {
    }

    public static Immutable getImmutableInstance(int a, int b) {
        return new Immutable(a, b);
    }
}
Run Code Online (Sandbox Code Playgroud)

Immutable你获得的每一个实例都将是真正不可变的 - 创建一个新的Immutable对其他实例Immutable没有影响,并且使用一个实例对任何其他实例都没有影响.

  • 同样,你在这里做的是对单身人员进行双重检查锁定.如果使用它的类不以线程安全的方式使用它,则关于"不可变"是否真正不可变的争论被抛出窗口.如果将不可变对象分配给可变引用,则该引用仍然不是线程安全的.不要将不变性与线程安全混为一谈. (2认同)