【发布时间】:2020-04-15 18:58:14
【问题描述】:
在阅读“Java 并发实践”后,在第 3.4 段中,它说
"
如果满足以下条件,则对象是不可变的:
• 构造后状态不可修改;和。
• 它的所有字段都是最终的;和。
• 它构造正确(this 引用不会在
建设)。
"
现在我有课了:
class A {
private final static ConcurrentMap<String, A> cache = new ConcurrentHashMap<>();
private final code;
private final name;
A(String code, String name) {
this.code = code;
this.name = name;
cache.put(code, this); // does this escaped? and make immutable A not thread safe anymore?
}
public static A getA(String code) {
return cache.get(code);
}
}
下面的链接似乎很有帮助,但我仍然感到困惑。
关联:
Does self-reference in the constructor counts as "escaping"?
总结:在上述情况下,在构造函数中,“this”是否会转义?不可变的 A 实例仍然是线程安全的吗?
另外,如果我将缓存变量移动到另一个类中,会发生什么?
【问题讨论】:
-
如果
cache持有对普通HashMap的引用而不是ConcurrentHashMap,那么答案肯定是肯定的。我假设在某处必须有一些其他代码调用A.cache.get(...)。 (如果您从不查找关联,那么将关联放入地图有什么意义?)如果该代码在一个线程中运行,而另一个线程构造了一个新的A实例,则缺少任何同步可能会导致第二个线程在能够看到分配给code和name的值之前获取对新实例的引用。 -
使用
ConcurrentHashMap有效地同步线程,以便第二个线程必须看到在put(...)调用之前发生的分配,如果它能够获取对象引用来自get(...)电话。但我不知道这是否被官方视为“安全发布”。 -
@SolomonSlow 感谢您的评论!是的,可以在任何线程中运行的其他代码访问缓存。我更改了代码以明确这一点
-
如果您对A使用工厂方法并将插入缓存中的内容移至工厂,则毫无疑问。
-
@LouisWasserman 我完全同意你的看法!但我仍然对这个案子感到好奇和困惑。
标签: java thread-safety