2011-11-05 155 views
14

当Guava的Tables.newCustomTable(Map, Supplier)方法提供线程安全映射时,会返回线程安全表吗?例如:Guava Table线程安全吗?

public static <R, C, V> Table<R, C, V> newConcurrentTable() { 
    return Tables.newCustomTable(
     new ConcurrentHashMap<R, Map<C, V>>(), 
     new Supplier<Map<C, V>>() { 
     public Map<C, V> get() { 
      return new ConcurrentHashMap<C, V>(); 
     } 
     }); 
} 

该代码是否实际返回并发表?

+0

你对“并发表”的定义是什么? –

+0

好问题。用另一种方式来解释我的问题:这些表会不会以一种并发地图>的方式炸毁?而通过“爆炸”,我的意思是进入无限循环,抛出异常,或者做任何其他常规HashBaseTable将做的事情,如果你试图在多线程上同时读取和写入它。 –

回答

19

来自doc:“如果多个线程同时访问此表,并且其中一个线程修改了表,则它必须在外部同步。”

并发备份集合是不够的。

+0

奇怪的是,javadoc中的那句话促使我问这个问题。好吧。谢谢,凯文。 –

16

Kevin Bourrillion是对的。您构建的地图不是线程安全的技术原因是,即使您使用的地图是线程安全的,表操作也可能不是。让我给放的例子,如StandardTable,这是使用Tables.newCustomTable实现:

public V put(R rowKey, C columnKey, V value) { 
    Map<C, V> map = backingMap.get(rowKey); 
    if (map == null) { 
    map = factory.get(); 
    backingMap.put(rowKey, map); 
    } 
    return map.put(columnKey, value); 
} 

线程安全的map == null案件处理损害。也就是说,两个或更多个线程可以进入该块并为columnKey创建新条目,并且执行backingMap.put(rowKey, map)的最后一个将最终覆盖backingMap中的columnKey的条目,这将导致丢失由其他执行的put操作线程。特别是在多线程环境下这种操作的结果是非确定性的,这相当于说这个操作不是线程安全的。

的正确实施此方法的是:

public V put(R rowKey, C columnKey, V value) { 
    ConcurrentMap<C, V> map = table.get(rowKey); 
    if (map == null) { 
     backingMap.putIfAbsent(rowKey, factory.get()); 
    } 
    map = backingMap.get(rowKey); 
    return map.put(columnKey, value); 
} 

我目前正在调查是否可以使用ForwardingTable实现与你想做的事一起,来获得足够的线程安全ConcurrentTable

但说实话,我认为没有线程安全的Table实现的原因是接口本身不提供任何并发结构,如putIfAbsentreplace

+1

在对StandardTable实现进行了大量浏览之后,我得出结论:为了提供线程安全性,您只有两个选择:从头开始创建实现,或者提供一个封装来锁定每个访问。其原因是由row(),column()或columnMap()返回的视图不是线程安全的,并且无法通过方法重写来修改它们的行为。 – velocipedist