是否需要同步构造函数中非线程安全集合的变异? [英] Does mutation of an non-thread-safe collection in a constructor need to be synchronized?

查看:132
本文介绍了是否需要同步构造函数中非线程安全集合的变异?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我决定使用非线程安全的集合并同步其访问权限,我是否需要同步构造函数中的任何变异?例如,在下面的代码中,我理解列表的引用对于构造后的所有线程都是可见的,因为它是最终的。但我不知道这是否构成安全发布,因为构造函数中的add不是同步的,而是在ArrayList的elementData数组中添加一个引用,这是非final.

If I decide to use a non-thread-safe collection and synchronize its access, do I need to synchronize any mutation in the constructor? For example in the following code, I understand the reference to the list will be visible to all threads post-construction because it is final. But I don't know if this constitutes safe publication because the add in the constructor is not synchronized and it is adding a reference in ArrayList's elementData array, which is non-final.

private final List<Object> list;

public ListInConstructor()
{
    list = new ArrayList<>();
    // synchronize here?
    list.add(new Object());
}

public void mutate()
{
    synchronized (list)
    {
        if (list.checkSomething())
        {
            list.mutateSomething();
        }
    }
}


推荐答案

好的,这就是JLS §17.5.1必须就此话题说。

Ok so this is what JLS §17.5.1 has to say on the topic.

首先:

设o为对象,c为o的构造函数,其中写入最终的
字段f。 当c退出时,正常或突然,对o的最终字段f进行冻结操作

Let o be an object, and c be a constructor for o in which a final field f is written. A freeze action on final field f of o takes place when c exits, either normally or abruptly.

所以我们知道在我们的代码中:

So we know that in our code:

public ListInConstructor() {
    list = new ArrayList<>();
    list.add(new Object());
} // the freeze action happens here!

所以现在有趣的部分:


给定写w,冻结f,动作a(不是读取
最终字段),f的最终字段的读取r1和读取r2
使得hb(w,f),hb(f,a),mc(a,r1)和dereferences(r1,r2),
然后在确定r2可以看到哪些值时,我们考虑
hb(w,r2)。

Given a write w, a freeze f, an action a (that is not a read of a final field), a read r1 of the final field frozen by f, and a read r2 such that hb(w, f), hb(f, a), mc(a, r1), and dereferences(r1, r2), then when determining which values can be seen by r2, we consider hb(w, r2).

所以让我们一次做一件:

So let's do this one piece at a time:

我们有hb(w,f),这意味着我们在离开构造函数之前到最终字段。

We have hb(w,f), which means we write to the final field before leaving the constructor.

r1是最终字段的读数和解引用(r1,r2)。这意味着r1读取最后一个字段,然后r2读取该最终字段的某个值。

r1 is the read of the final field and dereferences(r1, r2). This means that r1 reads the final field and r2 then reads some value of this final field.

我们还有一个动作(读或写,但不是最后一个字段的读数),它有hb(f,a)和mc(a,r1) 。这意味着动作发生在构造函数之后,但之后可以通过读取r1看到。

We further have an action (a read or write, but not a read of the final field) which has hb(f,a) and mc(a, r1). This means that the action happens after the constructor but can be seen by the read r1 afterwards.

因此它声明我们考虑hb(w,r2),这意味着写入必须在读取到用r1读取的最终字段的值之前发生。

And consequently it states that "we consider hb(w, r2)", which means that the write has to happen before the read to a value of the final field that was read with r1.

所以我看到它的方式很明显添加到列表中的对象必须可以被任何可以读取 list 的线程看到。

So the way I see it, it is clear that the object added to the list will have to be visible by any thread that can read list.

在旁注中:HotSpot通过在包含最终字段的任何构造函数的末尾放置内存屏障来实现最终字段语义,从而保证这一点无论如何都是财产。这只是一个优化(最好只做一个屏障,尽可能远离写入)是另一个问题。

On a sidenote: HotSpot implements final field semantics by putting a memory barrier at the end of any constructor that contains a final field thereby guaranteeing this property in any case. Whether that's just an optimisation (Better to do to only a single barrier and that as far away from the write as possible) is another question.

这篇关于是否需要同步构造函数中非线程安全集合的变异?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆