Dominik
Dominik

Reputation: 1252

Synchronization of Nested Data Structures between Threads in Java

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.

Upvotes: 0

Views: 617

Answers (3)

Yu Sun corn
Yu Sun corn

Reputation: 616

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)

Upvotes: 1

Rustum
Rustum

Reputation: 55

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

Upvotes: 0

templatetypedef
templatetypedef

Reputation: 372814

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.

Upvotes: 1

Related Questions