ConcurrentHashMap是否会通过get()公开部分构造的对象?

时间:2017-08-21 14:18:12

标签: java concurrency concurrenthashmap

JavaDoc for ConcurrentHashMap states

  

检索操作(包括get)一般不会阻塞,因此可能与更新操作重叠(包括put和remove)。检索反映了最近完成的更新操作的结果。 (更正式地说,给定密钥的更新操作与报告更新值的该密钥的任何(非空)检索之前发生关系。)

因为"给定密钥的更新操作...在...之前发生...对该密钥的任何(非空)检索"并且部分构造的对象被视为非null,*以下代码是否可能允许Thread2访问部分构造的对象?

Thread1

// Immutable Object (all fields final)
concurrentHashMap.put("immutableObject", new ImmutableObject());

// Volatile Object (all fields volatile)
concurrentHashMap.put("volatileObject", new VolatileObject());

// Thread-safe Mutable Object
concurrentHashMap.put("mutableObject", new MutableObject());

Thread2

concurrentHashMap.get("immutableObject");
concurrentHashMap.get("volatileObject");
concurrentHashMap.get("mutableObject");

是否需要在这些对象的构造函数中执行某种同步,以确保在完全初始化之前没有线程访问它们?

*我并非100%确定部分构建的对象被认为是非空的,但我还没有看到任何相反的证据。似乎因为线程可以访问部分初始化的对象,所以部分初始化的对象不能为空,因为可以访问一些内部数据(无论是初始化的)。

3 个答案:

答案 0 :(得分:6)

在您的示例中,您永远不会将部分构造的对象添加到地图中 在传递给方法之前评估参数。

15.12.4. Run-Time Evaluation of Method Invocation JLS确实表明参数表达式(第二步)在方法执行之前进行评估(最后一步):

  

在运行时,方法调用需要五个步骤。首先是目标   可以计算参考。 其次,参数表达式是   评价即可。第三,要调用的方法的可访问性是   检查。第四,要执行的方法的实际代码是   位于。 第五,创建一个新的激活帧,同步是   必要时执行,并将控制转移到方法代码

所以这里:

concurrentHashMap.put("immutableObject", new ImmutableObject());
评估

new ImmutableObject()并将ImmutableObject对象完全构建,然后将其传递给concurrentHashMap.put(..)方法。

答案 1 :(得分:1)

put的调用将逐个阻塞,直到每个构造函数完成,从而使get操作成为原子。

因此,如果put调用不是null调用的顺序调用,他们可以在键值对已经存在的情况下查询地图,也可以不生成Map<String, Foo> map = new ConcurrentHashMap<>(); map.put("", new Foo()); class Foo { Foo() { while (true) {} } }

这是一个将永远运行的最小例子,用于说明:

<MyControl>
    <Grid>
        <Grid.ColumnDefinitions>...</Grid.ColumnDefinitions>
        <ToggleButton />
        <ToggleButton />
        <ToggleButton />
        ...
        <ToggleButton />
    </Grid>
</MyControl>

答案 2 :(得分:0)

concurrentHashMap.put("immutableObject", new ImmutableObject());

这一行按以下顺序执行两项操作:

  1. 创建一个新的ImmutableObject
  2. 将创建的对象添加到hashmap
  3. 所以答案是否定的,hashmap不能返回部分构造的对象,因为在tehy被添加到hashmap之前完全构造了对象。