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

发布于 2024-10-16 07:00:35 字数 1326 浏览 2 评论 0原文

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

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;
  }
}

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

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

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

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

I have a cache implementation like this:

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;
  }
}

Is this implementation thread-safe? Can I be sure to get the last updated state from any thread inside the synchronized block? Would this behaviour change if I use a java.util.concurrent.ReentrantLock instead of a synchronized block, like this:

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

I know that final instance members are synchronized between threads, but is this also true for the objects held by these members?

Maybe this is a dumb question, but I don't know how to test it to be sure, that it works on every OS and every architecture.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

止于盛夏 2024-10-23 07:00:35

对于初学者来说,这不是一个愚蠢的问题。同步确实很难做到正确,而且我并不自称是这方面的专家。

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

简而言之,您不应该拥有一个由多个线程读取或写入的结构,除非您使用同步原语(如 synchronized 或锁)来保护它,或者除非该结构是专门设计用于像 ConcurrentHashMap 一样是无锁的。

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

For starters, this isn't a dumb question. Synchronization is really hard to get right, and I don't profess to be an expert in it.

In your program, at the indicated context, yes, you can assume that the String you're getting is the most updated version. However, your code is still not safe because you are reading a value from the Map outside of the synchronized block. If this read occurs at the same time that the Map is having a value inserted into it, you're not guaranteed to get back a sensible value. I know that on at least some implementations, this can cause an infinite loop due to some weirdness in the implementation.

The short version is that you should not have a structure that is read from or written to by multiple threads unless you guard it with a synchronization primitive like synchronized or a lock, or unless that structure is specifically designed to be lock-free like the ConcurrentHashMap.

You could indeed use the ReentrantLock in this case to guard access to the structure and to do a timed wait, but if you do you'd have to guarantee that any reads of the structure were also guarded by the same lock. Otherwise you risk multiple threads seeing inconsistent or corrupted data.

七分※倦醒 2024-10-23 07:00:35

在类 X 实现的情况下,innerStructure 的值可以在值不为 null 时更新。所以假设innerStructure的所有值都不为null。在此假设下,如果在没有 getValue() 的情况下结构和 insideStructure 未更新,则这是线程安全的。

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

虽然看起来性能的提升是一个目的,
以下站点描述了该问题和解决方法。

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

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

In the case of class X implementation, a value of innerStructure can be updated just when the value is not null. So it is assumed that all value of innerStructure is not null. On this assumption if the structure and the innerStructure is not updated without getValue(), this is thread safe.

If the structure and the innerStructure is updated at other function, the value that getValue returns has the possibility of not becoming it in the latest value.

Though it seems that the improvement of the performance is a purpose,
the problem and the workaround are described on the following site.

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

(Does this fix the double-checked locking problem)

蛮可爱 2024-10-23 07:00:35

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

You might need to use ConcurrentHashMap for outer Hashmap and InnerHashMap for synchronized get and put operations at Bucket level

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文