我想确保我正确理解根据Java内存模型的‘有效不可变对象’行为。
假设我们有一个可变类,我们希望将它发布为一个有效不可变的类:
class Outworld {
// This MAY be accessed by multiple threads
public static volatile MutableLong published;
}
// This class is mutable
class MutableLong {
private long value;
public MutableLong(long value) {
this.value = value;
}
public void increment() {
value++;
}
public long get() {
return value;
}
}
我们执行以下操作:
// Create a mutable object and modify it
MutableLong val = new MutableLong(1);
val.increment();
val.increment();
// No more modifications
// UPDATED: Let's say for this example we are completely sure
// that no one will ever call increment() since now
// Publish it safely and consider Effectively Immutable
Outworld.published = val;
问题是:Java内存模型是否保证所有线程必须具有outworld.published.get()==3
?
根据Java并发实践,这应该是正确的,但如果我错了,请纠正我。
3.5.3.安全发布习语
要安全地发布对象,必须同时使对对象的引用和对象的状态对其他线程可见。通过以下方法可以安全地发布正确构造的对象:
-从静态初始化器初始化对象引用;
-将对它的引用存储到volatile字段或atomicreference中;
-将对它的引用存储到正确构造的对象的final字段中;或者
-将对它的引用存储到由锁正确保护的字段中。
3.5.4.有效不可变的对象
安全发布的有效不可变对象可以被任何线程安全地使用,而不需要额外的同步。
是的。对mutablelong
的写操作在读之前,后面紧跟一个happens-before
关系(在volatile上)。
(有可能一个线程读取outworld.publish
并不安全地将其传递给另一个线程。理论上,这可能会看到更早的状态。但实际上,我并不认为会发生这种情况。)
问题内容: 这是Java Concurrency in Practice中的一句话 共享的只读对象包括不可变的和实际上不可变的对象。 不变对象和有效不变对象之间有什么区别? 问题答案: 不可扩展且其字段全部为自身且不可变的类的实例是不可变的。 由于其方法的详细信息而无法更改其字段的类的实例实际上是不可变的。例如: 的某些实例实际上是不可变的,而有些则不是。 另一个例子是零长度数组。它们实际上是不可
问题内容: 我想确保根据Java内存模型正确理解“有效不可变对象”的行为。 假设我们有一个可变的类,我们希望将其发布为有效的不可变的类: 我们执行以下操作: 问题是 :Java内存模型是否保证所有线程都必须具有? 根据 Java Concurrency In Practice, 这应该是正确的,但是如果我错了,请更正我。 3.5.3。安全出版惯用语 为了安全地发布对象,必须同时使对该对象的引用和该
问题内容: 我正在努力使可变对象与不可变对象有关。使用可变对象会带来很多负面影响(例如,从方法中返回字符串数组),但是我很难理解它的负面影响。使用可变对象的最佳实践是什么?您是否应尽可能避免使用它们? 问题答案: 好吧,这有几个方面。 没有参考身份的可变对象会在奇数时间导致错误。例如,考虑使用基于值的方法的 : 当实例用作键时,实例在映射中“丢失”,因为实例和相等性基于可变值。这些值在映射之外更改
所以我读了关于HashMap的文章。有人曾指出: 我不太明白...为什么?
第3.5.4节讨论了有效不可变的对象,即一旦一个对象被安全地完全构造,它的状态就不会被任何代码路径的任何代码所改变。 戈茨爵士举了一个例子: 总而言之,有效不可变对象的前提并不需要任何线程安全容器,因为我们不应该在任何代码路径中为有效不可变对象设置任何赋值代码。
问题内容: 因此,我了解了HashMap。有人指出: “不可变性还允许缓存不同键的哈希码,这使整个检索过程非常快,并且表明String和Java Collection API提供的各种包装器类(例如)都是非常好的键。” 我不太明白…为什么? 问题答案: : 由于永不更改的内容,因此该类的创建者选择在对哈希进行一次计算之后就对其进行缓存。这样,不会浪费时间重新计算相同的值。