4

我有一个CHM定义如下。每当有任何更新时,我从单个后台线程调用setDataProcess方法。我总是从多个阅读器线程呼叫getDataMappingConcurrentHashMap更改对所有线程都可见吗?

private static final ConcurrentHashMap<ProcessType, Mapping> mappingsHolder = new ConcurrentHashMap<ProcessType, Mapping>(); 
private static final CountDownLatch hasInitialized = new CountDownLatch(ProcessType.values().length); 

public static void setDataProcess(ProcessType processType, Mapping newMapData) { 
    mappingsHolder.put(processType, newMapData); 
    hasInitialized.countDown(); 
} 

public static Mapping getDataMapping(ProcessType processType) { 
    try { 
     hasInitialized.await(); 
     return mappingsHolder.get(processType); 
    } catch (InterruptedException e) { 
     Thread.currentThread().interrupt(); 
     throw new IllegalStateException(e); 
    } 
} 

问题是 - 在mappingsHolder CHM的任何变化将是可见的所有读者线程立即或我需要使用挥发性的是什么?

+0

试看看有多难? – Alp

+1

@Alp:对于可见性问题,尝试看是非常平凡的。特别是,如果它失败了,它可能在不同的硬件上,比x86更松散的规则。 –

+0

您是否使用了CountDownLatch,因此只有在填充mappingsHolder后才能调用getDataMapping? – adhg

回答

4

就我所见,您在读取方法中使用CountDownLatch,直到计数达到零。计数显然是ProcessType中的枚举常量的数量。如果这个CountDownLatch正确实施,不应该有一个问题,因为根据javadocs,应该有一个之前发生关系:

内存一致性效果:直到计数达到零,行动线程之前致电countDown()发生在之前发生的动作,在另一个线程中成功从相应的await()返回。

忽略CountDownLatch因素,ConcurrentHashMap不会同步上的一个键的检索,但检索对应于按键的最新值。

检索操作(包括get)一般不阻塞,因此,可以与更新操作(包括putremove)重叠。 检索反映了最近的完成的更新操作的结果。

这意味着线程所做的更改对于读取密钥的线程是可见的。但是这两者可能会干涉。

+0

CDL是在启动过程中进行一次性同步,但之后必须进行原子更新,以便CHM更改对所有线程均可见? – john

+0

感谢您的编辑,这意味着它将成为原子更新,对CHM的任何更改都将对所有线程都可见? – john

+0

@david根据文档可以看到更改。 – manouti

0

由于变量(即地图内存中的位置)的值不会改变,因此我怀疑volatile会在这里产生影响。地图对象一旦被分配,将不会四处移动。

但是,一如既往存在潜在的时间问题:线程可能会在您更改之前或之后读取值。基本上,一个concurrentHashMap将不会被破坏被一次使用多个线程。因此,这是一个必要的功能。但是,这并不能真正解决线程为成功协同工作可能需要的任何其他同步或通知要求。

2

CountdownLatch确保countDown之前的动作发生在另一个线程从await返回之前,如Manouti said

对于ConcurrentHashMap中,the api documentation写着:

对于给定密钥的更新操作蕴藏着之前发生的任何(非空)检索该键报告经更新的价值关系。

它应该处理地图变化对线程读取的可见性。

相关问题