Java中线程之间嵌套数据结构的同步

时间:2011-02-09 06:58:19

标签: java multithreading synchronization

我有一个像这样的缓存实现:

class X
{
  private final Map<String, ConcurrentMap<String, String>> structure = new HashMap...(); 

  public String getValue(String context, String id)
  {
     // just assume for this example that there will be always an innner map
     final ConcurrentMap<String, String> innerStructure = structure.get(context);

     String value = innerStructure.get(id);
     if(value == null)
     {
       synchronized(structure)
       {
          // can I be sure, that this inner map will represent the last updated
          // state from any thread?
          value = innerStructure.get(id);
          if(value == null)
          {
            value = getValueFromSomeSlowSource(id);
            innerStructure.put(id, value); 
          }
       }
     }       
     return value;
  }
}

这个实现是否是线程安全的?我可以确定从synchronized块中的任何线程获取最后更新的状态吗?如果我使用java.util.concurrent.ReentrantLock而不是synchronized块,这种行为是否会改变,如下所示:

...
if(lock.tryLock(3, SECONDS))
{
  try
  {
    value = innerStructure.get(id);
    if(value == null)
    {
      value = getValueFromSomeSlowSource(id);
      innerStructure.put(id, value); 
    }
  }
  finally
  {
    lock.unlock();
  }
}
...

我知道最终实例成员在线程之间是同步的,但这对于这些成员持有的对象也是如此吗?

也许这是一个愚蠢的问题,但我不知道如何测试它以确保它适用于每个操作系统和每个架构。

3 个答案:

答案 0 :(得分:1)

首先,这不是一个愚蠢的问题。同步真的很难做到,而且我并不自称是专家。

在您的程序中,在指定的上下文中,是的,您可以假设您获得的String是最新版本。但是,您的代码仍然不安全,因为您正在从Map块之外的synchronized读取值。如果此读取发生在Map插入值的同时,则无法保证返回合理的值。我知道至少在某些实现中,由于实现中的一些奇怪现象,这可能导致无限循环。

简短版本是你不应该有一个由多个线程读取或写入的结构,除非你使用synchronized或锁等同步原语来保护它,或者除非该结构专门设计为像ConcurrentHashMap一样无锁。

在这种情况下,您确实可以使用ReentrantLock来保护对结构的访问并进行定时等待,但是如果您这样做,则必须保证结构的任何读取也受到保护。同样的锁。否则,您会冒多线程看到数据不一致或损坏的风险。

答案 1 :(得分:1)

在X类实现的情况下,只要值不为null,就可以更新innerStructure的值。因此,假设innerStructure的所有值都不为空。假设在没有getValue()的情况下没有更新结构和innerStructure,这是线程安全的。

如果结构和innerStructure在其他函数中更新,则getValue返回的值有可能不会将其变为最新值。

虽然看起来性能的改善是一个目的, 问题和解决方法在以下网站上描述。

http://www.ibm.com/developerworks/java/library/j-jtp03304/

(这是否解决了双重检查锁定问题)

答案 2 :(得分:0)

您可能需要将ConcurrentHashMap用于外部Hashmap和InnerHashMap,以便在Bucket级别同步获取和放置操作