这是来自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]这里的问题不是Holder类本身,而是Holder没有正确发布。但是,可以通过将n字段声明为final来使Holder免受不适当发布的影响,这将使Holder不变。
从这个答案:
final的规范(请参阅@andersoj的答案)保证当构造函数返回时,将对final字段进行适当的初始化(从所有线程可见)。
从维基:
例如,在Java中,如果已经内联了对构造函数的调用,则一旦分配了存储空间,但是在内联的构造函数初始化对象之前,可以立即更新共享变量。
我的问题是:
因为:(可能不正确,我不知道。)
a)可以在内联构造函数初始化对象之前立即更新共享变量。
b)只有在构造函数返回时,才能保证对final字段进行正确的初始化(从所有线程可见)。
另一个线程是否可能看到默认值holder.n
?(即,另一个线程holder
在holder
构造函数返回之前获取对它的引用。)
如果是这样,那么您如何解释以下声明?
通过将n字段声明为final,可以使Holder免受不适当发布的影响,这将使Holder不可变
编辑: 从JCiP。不可变对象的定义:
在以下情况下对象是不可变的:
x构造后无法修改其状态;x其所有字段均为最终字段; [12]和
x构造正确(该参考在构造期间不会逸出)。
因此,根据定义,不可变对象没有“ this
引用转义”问题。对?
但是如果不声明为易失性,它们是否会遭受双重检查锁定模式的乱序写入?
一个不可变的对象,例如String
,对于所有读者来说似乎都具有相同的状态,而不管它的引用如何获得,即使同步不正确并且没有先发生后关系。
这是通过final
Java5中引入的字段语义实现的。通过最后一个字段进行的数据访问具有更强的内存语义,如jls-17.5.1中所定义。
在编译器重新排序和内存障碍方面,处理最终字段时存在更多约束,请参阅《JSR-133
Cookbook》
。您担心的重新排序不会发生。
是的-可以通过包装器中的final字段进行双重检查锁定;没有volatile
要求!但是这种方法不一定更快,因为需要两次读取。
请注意,此语义适用于各个最终字段,而不适用于整个对象。例如,String
包含一个可变字段hash
;但是,String
由于其公共行为仅基于final
字段,因此被认为是不变的。
final字段可以指向可变对象。例如,String.value
a char[]
是可变的。要求不可变的对象是最终字段的树是不切实际的。
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)
。
不 通过最终字段进行访问 不会 - foo.list.get(2)
通过不正确的发布是不安全的,即使它读取的是同一目的地。
这些就是设计动机。现在让我们看看JLS如何在jls-17.5.1中对其进行形式化
freeze
在构造函数的出口处定义了一个动作,与在final字段的分配中定义的动作相同。这使我们可以在构造函数内部的任何地方编写以填充内部状态。
不安全发布的常见问题是缺少“事前”(hb
)关系。即使读取看到写入,它也不会与其他操作建立任何关系。但是,如果易失性读取看到易失性写入,则JMM会hb
在许多操作之间建立顺序。
该final
领域的语义想要做同样的事情,即使是正常的甚至通过不安全的出版物读取和写入,也就是。为此mc
,在读取可见的任何写入之间添加一个存储链()顺序。
甲deferences()
为了限制了语义访问 通过 最后一个字段。
让我们重新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)
因此w
是可见的r2
。
本质上,通过Foo
包装器,可以通过不安全的发布安全地发布地图(本身是可变的)……如果这是有道理的。
我们可以使用包装器建立最终字段的语义,然后丢弃它吗?喜欢
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
。
问题内容: C#核心库中是否内置可以为我提供不可变字典的内容? 类似于 Java的 东西: 只是为了澄清一下,我并不是要阻止键/值本身被更改,而只是希望字典的结构不会停止更改。如果将IDictionary的任何mutator方法称为(),我都会希望它们快速而响亮地失败。 问题答案: 不,但是包装器很简单: 显然,如果要允许修改值,可以更改上面的this []设置器。
我想创建一个包含XFA数据的PDF,但我不想在查看PDF时看到XFA数据,但是,我希望可以通过以下代码访问它: 我怎么能这样呢? 谢谢
问题内容: 我知道这可能很愚蠢,但是很多地方都声称Java中的Integer类是不可变的,但是下面的代码: 毫无困难地执行(给出预期的结果)6。因此有效地改变了a的值。这不是说Integer是可变的吗?第二个问题和一些小问题:“不变的类不需要复制构造函数”。有人在乎解释原因吗? 问题答案: 不可变并不意味着永远不能等于另一个值。例如,也是不可变的,但是我仍然可以这样做: 并没有改变,而是变成了一个
问题内容: 在我们的应用程序中,我们需要具有只能分配一次的字段。 最初,我们想到封装字段并将设置程序设为私有。但是,引起一些问题: 如果没有公共设置者,Hibernate是否仍然可以从数据库映射字段? 我是否可以剥离设置器并使字段仅在实体构造函数中可变? 最后,是否有任何标准的JPA方法使字段不变? 提前致谢。 问题答案: 广告。1:我相信,如果将注释放在字段而不是在getter上,则JPA会将普
我尝试遵循在工具栏中创建自定义TextView的教程(https://guides.codepath.com/android/using-the-app-toolbar#custom-title-view)。我想做的是能够使用Java动态更改TextView中的文本。但是,问题是我不能并且没有显示错误消息。 XML 中的工具栏代码: 活动类中的工具栏Java代码: Styles.xml: 一个简单
问题内容: 我正在尝试使用Spring 3.0.6返回JSON响应,但是得到了406响应“ Not Acceptable”,其描述为:“此请求所标识的资源仅能够生成具有以下特征的响应:请求“接受”标头()。” 我知道之前曾问过一个非常类似的问题,但尽管进行了许多测试,但我无法使它适用于我的项目,而且我不知道自己在做什么错。 在我的Maven pom.xml中,执行以下操作: 在web.xml中,我