【问题标题】:Does the this reference escape the constructor even on the last line?即使在最后一行,this 引用也会转义构造函数吗?
【发布时间】: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 实例,则缺少任何同步可能会导致第二个线程在能够看到分配给codename 的值之前获取对新实例的引用。
  • 使用ConcurrentHashMap 有效地同步线程,以便第二个线程必须看到在put(...) 调用之前发生的分配,如果它能够获取对象引用来自get(...) 电话。但我不知道这是否被官方视为“安全发布”。
  • @SolomonSlow 感谢您的评论!是的,可以在任何线程中运行的其他代码访问缓存。我更改了代码以明确这一点
  • 如果您对A使用工厂方法并将插入缓存中的内容移至工厂,则毫无疑问。
  • @LouisWasserman 我完全同意你的看法!但我仍然对这个案子感到好奇和困惑。

标签: java thread-safety


【解决方案1】:

从技术上讲,this 引用确实 转义了构造函数,因为它被传递到缓存在构造函数返回之前。也就是说,缓存接收到对尚未完全初始化的A 实例的引用。通过此缓存同时访问该实例的任何人都将能够观察到尚未完全初始化的该实例。由于cache.put 是构造函数中的最后一行,这在实践中可能并不重要。但我最好不要指望它,它会在Asubclassed 时中断。

【讨论】:

    猜你喜欢
    • 2020-10-18
    • 2011-07-02
    • 2020-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多