通过构造函数发布对象

时间:2016-01-08 08:07:42

标签: java multithreading synchronization

考虑以下课程:

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转义?

2 个答案:

答案 0 :(得分:2)

您误解了答案。

让我们了解{strong>构造函数中synchronized(this)的作用。

要使synchronized(this)语句有用,两个线程应该同时使用同一个对象访问同步块。

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

对于某个其他线程来引用该对象,应该从某个地方的构造函数中泄漏当前对象(this

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

答案 1 :(得分:1)

答案意味着使用synchronize(this)的唯一原因是this引用已从构造函数中转义。

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

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

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

但是:你是如何将Ideone的实例从一个线程传递到另一个线程的?如果你在一个没有任何同步机制的字段中这样做,那么没有任何先发生关系,那么第二个线程就不能保证完全看到Ideone的整个实例。

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

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

由于在线程之间传递对象的所有安全机制都涉及建立先发生关系的同步机制,因此(几乎)不必在构造函数中对getM执行操作。