gpt4 book ai didi

java - 不可变对象(immutable对象)是否不受不当发布的影响?

转载 作者:行者123 更新时间:2023-12-01 22:43:13 26 4
gpt4 key购买 nike

这是 JCiP 中的一个示例.

public class Unsafe {
// Unsafe publication
public Holder holder;

public void initialize() {
holder = new Holder(42);
}
}

public class Holder {
private int n;

public Holder(int n) {
this.n = n;
}
public void assertSanity() {
if (n != n) {
throw new AssertionError("This statement is false.");
}
}
}

第 34 页:

[15] The problem here is not the Holder class itself, but that the Holder is not properly published. However, Holder can be made immune to improper publication by declaring the n field to be final, which would make Holder immutable;



来自 this answer :

the specification for final (see @andersoj's answer) guarantees that when the constructor returns, the final field will have been properly initialized (as visible from all threads).



来自 wiki :

For example, in Java if a call to a constructor has been inlined then the shared variable may immediately be updated once the storage has been allocated but before the inlined constructor initializes the object



我的问题是:

因为:(可能是错的,我不知道。)

a) 可以在内联构造函数初始化对象之前立即更新共享变量。

b) 只有在构造函数返回时,才能保证最终字段被正确初始化(从所有线程可见)。

是否有可能另一个线程看到 holder.n 的默认值? ? (即,另一个线程在 holder 构造函数返回之前获得对 holder 的引用。)

如果是这样,那么你如何解释下面的陈述?

Holder can be made immune to improper publication by declaring the n field to be final, which would make Holder immutable



编辑:
来自 JCiP。不可变对象(immutable对象)的定义:

An object is immutable if:
x Its state cannot be modified after construction;

x All its fields are final;[12] and

x It is properly constructed (the this reference does not escape during construction).



因此,根据定义,不可变对象(immutable对象)不存在“ this 引用转义”问题。对?

但是他们会遭受 Out-of-order writes如果未声明为 volatile ,是否处于双重检查锁定模式?

最佳答案

一个不可变的对象,例如String , 似乎对所有读者都具有相同的状态,无论它的引用是如何获得的,即使同步不正确和缺乏发生前的关系。

这是通过 final 实现的Java 5 中引入的字段语义。通过最终字段的数据访问具有更强的内存语义,如 jls-17.5.1 中所定义。

在编译器重新排序和内存屏障方面,在处理 final 字段时有更多的限制,请参阅 JSR-133 Cookbook .您担心的重新排序不会发生。

是的——双重检查锁定可以通过包装器中的 final 字段来完成;没有 volatile是必须的!但这种方法不一定更快,因为需要两次读取。

请注意,此语义适用于单个最终字段,而不是整个对象。例如,String包含可变字段 hash ;尽管如此,String被认为是不可变的,因为它的公共(public)行为仅基于 final字段。

final 字段可以指向一个可变对象。例如,String.valuechar[]这是可变的。要求不可变对象(immutable对象)是最终字段树是不切实际的。

final char[] value;

public String(args) {
this.value = createFrom(args);
}

只要我们不修改 value的内容构造函数退出后,就可以了。

我们可以修改 value的内容在构造函数中以任何顺序,都没有关系。
public String(args) {
this.value = new char[1];
this.value[0] = 'x'; // modify after the field is assigned.
}

另一个例子
final Map map;
List list;

public Foo()
{
map = new HashMap();
list = listOf("etc", "etc", "etc");
map.put("etc", list)
}

任何访问 通过 final 字段似乎是不可变的,例如 foo.map.get("etc").get(2) .

不通过 final 字段访问不会 -- foo.list.get(2)通过不正确的发布是不安全的,即使它读取相同的目的地。

这些是设计动机。现在让我们看看 JLS 如何在 jls-17.5.1 中将其形式化。

一个 freeze action 是在构造函数导出处定义的,与在 final 字段的分配处相反。这允许我们在构造函数内的任何地方编写来填充内部状态。

不安全发布的常见问题是缺少happens-before ( hb) 关系。即使读取看到写入,它也不会建立任何其他操作。但是,如果 volatile 读取看到 volatile 写入,JMM 会建立 hb以及许多 Action 之间的顺序。
final字段语义想要做同样的事情,即使是正常的读写,也就是说,即使是通过不安全的发布。为此,在读取看到的任何写入之间添加一个内存链 ( mc) 顺序。

一个 deferences() order 将语义限制为访问 通过最后的领域。

让我们重温 Foo示例看看它是如何工作的
tmp = new Foo()

[w] write to list at index 2

[f] freeze at constructor exit

shared = tmp; [a] a normal write

// Another Thread

foo = shared; [r0] a normal read

if(foo!=null) // [r0] sees [a], therefore mc(a, r0)

map = foo.map; [r1] reads a final field

map.get("etc").get(2) [r2]

我们有
hb(w, f), hb(f, a), mc(a, r1), and dereferences(r1, r2)

因此 wr2 可见.

本质上,通过 Foo包装器,一个 map (它本身是可变的)通过不安全的发布安全地发布......如果这是有道理的。

我们可以使用包装器建立最终字段语义然后丢弃它吗?像
Foo foo = new Foo();   // [w] [f]

shared_map = foo.map; // [a]

有趣的是,JLS 包含足够的子句来排除此类用例。我猜它被削弱了,因此允许更多的内线程优化,即使是最终字段。

请注意,如果 this在卡住操作之前泄漏,最终字段语义无法保证。

但是,我们可以安全地泄漏 this在卡住操作之后的构造函数中,带有构造函数链接。
-- class Bar

final int x;

Bar(int x, int ignore)
{
this.x = x; // assign to final
} // [f] freeze action on this.x

public Bar(int x)
{
this(x, 0);
// [f] is reached!
leak(this);
}

x 而言,这是安全的被关注到;卡住对 x 的操作在构造函数的存在处定义,其中 x被安排了。这可能只是为了安全泄漏而设计的 this .

关于java - 不可变对象(immutable对象)是否不受不当发布的影响?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35167777/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com