【问题标题】:Object publication through constructor通过构造函数发布对象
【发布时间】:2016-01-08 08:07:41
【问题描述】:

考虑以下类:

class Ideone
{
    private Map<String, String> m;
    public Ideone(){
        synchronized(this){
            m = new ConcurrentHashMap<>();
        }
    }

    public synchronized Map<String, String> getM(){
        return Collections.unmodifiableMap(m); //effectively immutable
    }
}

为了让其他类观察Ideone的内部状态,我们应该安全地发布它的内部状态(正确同步)。如果我们不这样做,则不能保证另一个线程读取正确的值(不是默认值)。例如:

public volatile Ideone ideone;

public void init(){
    ideone = new Ideone();
}

我想如果我们不同步构造和吸气剂,就像

class Ideone
{
    private Map<String, String> m;
    public Ideone(){
            m = new ConcurrentHashMap<>();
    }

    public Map<String, String> getM(){
        return Collections.unmodifiableMap(m); //effectively immutable
    }
}

无法保证观察到正确的状态值(例如默认值)。

但正如this answer 中所说,只要允许this 逃逸,这种同步是不可取的。

问题:为什么构造函数中的同步允许this 转义?

【问题讨论】:

    标签: java multithreading synchronization


    【解决方案1】:

    你误解了答案。

    让我们了解synchronized(this)构造函数中做了什么。

    为了使synchronized(this) 语句有用,两个线程应该同时访问具有相同对象的同步块。

    现在,其中一个线程在构造函数中,这意味着对象刚刚被创建...

    如果其他线程有对该对象的引用,您应该在某个地方从构造函数中泄露了当前对象 (this)

    您的代码不会泄漏它,但synchronized(this) 在您的代码的构造函数中没有任何值。

    【讨论】:

      【解决方案2】:

      答案意味着使用synchronize(this) 的唯一原因是this 引用从构造函数中逃逸的情况。

      但这种推理本身是不正确的。我为这个问题添加了另一个答案:https://stackoverflow.com/a/34672811/981744

      您已经展示了一个案例,乍一看,您在构造函数中使用synchronized(this) 是合理的,因为它确实确保了getM() 在从另一个线程调用时实例变量m 的正确值。

      如果您没有这些同步块,则不一定是这种情况 - 即使在构造函数完成后,另一个线程也有可能看到字段 m 的值 m,因为在一个线程的构造函数中对m 的赋值与在另一个线程的方法getM 中读取m 之间没有发生之前的关系。

      但是:你是如何将Ideone 的实例从一个线程传递到另一个线程的?如果您在没有任何同步机制的字段中执行此操作,因此没有任何发生之前的关系,则根本无法保证第二个线程看到Ideone 的整个实例。

      如果是看到实例,那么实例中的数据如果正确,但在这种情况下它也可能看到值null

      但是,如果您确实使用了同步机制来传递Ideone 的实例,那么该机制已经创建了一个happens-before 关系,并且您在构造函数和getM 中使用synchronized 是不必要的没有了。

      由于在线程之间传递对象的所有安全机制都涉及一种同步机制,该机制设置了先发生关系,因此(几乎)不需要在构造函数中使用 synchronized 进行操作。

      【讨论】:

      • 那么,仅仅将Ideone 声明为volatile 不足以观察对象的一致状态,对吗?
      • @St.Antario 您的意思是将m 声明为易失性?是的,具有相同的效果,在不同的线程上写入 volatile 和读取 volatile 也具有发生前的关系。
      猜你喜欢
      • 2013-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-01
      • 1970-01-01
      相关资源
      最近更新 更多