6

If I have multiple threads accessing the getter and setter, is this code going to run into any race condition? I do not mind the getter gets the old data during the set operation, but as long as it does not cause an exception or got null.

ConcurrentHashMap<String, Object> hashMap =
    new ConcurrentHashMap<String, Object> ();

void setByteArray(String string, byte[] byteArray) {
    hashMap.put(string, byteArray.clone());
}

byte[] getByteArray(String string) {
    return ((byte[]) hashMap.get(string)).clone();
}
7
  • 1
    Yes, this code is almost thread-safe, but don't forget to declare hashmap private and final.So if someone creates the subclass, he wouldn't be able to break encapsulation and thread safety. Commented Nov 20, 2012 at 9:25
  • 1
    cloning byte[] can be very expensive if done often. I assume you are using multiple threads to improve performance so I would consider a strategy which avoids the need to clone byte[]s Commented Nov 20, 2012 at 9:25
  • 1
    I'd suggest to check that hashMap.get(string) != null before cloning Commented Nov 20, 2012 at 9:30
  • @BorisTreukhov, subclasses will not be able to break thread-safety. The instantiated object is a ConcurrentHashMap. Commented Nov 20, 2012 at 9:34
  • 1
    @Nerrve see stackoverflow.com/questions/12177081/… So alas the code in the question seems to be not quite threadsafe. Commented Nov 20, 2012 at 9:36

4 Answers 4

5

This is almost thread-safe (if there is such a thing). The only thing missing is to declare the hashMap field final. This guarantees safe publication of the map.

Other than that, I don't see any problems (regarding thread-safety). ConcurrentHashMap is thread safe, so storing and retrieving the byte arrays should be as well.

Also, since you always copy the byte arrays, they will never be shared among threads, except for the ones that are stored in the Map. The ConcurrentHashMap will safely publish those to all threads and since they are never modified (meaning they're effectively immutable), thread safety is guaranteed.

Finally, based on the comments, here is an improved version regarding some other aspects:

private final ConcurrentHashMap<String, Object> hashMap =
    new ConcurrentHashMap<String, Object> ();

void setByteArray(String string, byte[] byteArray) {
    hashMap.put(string, byteArray.clone());
}

byte[] getByteArray(String string) {
    Object result = hashMap.get(string);
    if(result == null)
        return null;
    else
        return ((byte[]) result).clone();
}

The first thing is the private modifier for hashMap, so subclasses can not store any other objects, for example shared byte arrays.

The second thing is the null check in the getter. You might want to replace return null; by throw new IllegalArgumentException(); or something else, based on your requirements.

Sign up to request clarification or add additional context in comments.

3 Comments

final is not only about replacing the reference but also about memory visibility see M. Topolnik answer : stackoverflow.com/questions/12177081/…
I am curious, how is getByteArray( .. ) thread safe? It is performing a "check then act" compound action which is not thread-safe. The entire action needs to be "locked" or the method can simply return "hashMap.get( )" and the calling thread can then perform the check and clone part ...
@CaptainHastings "Check then act" is only wrong if the property that is checked could change before the act. This is not possible here because result is a local variable and the property is nullness.
0

ConcurrentHashMap is set to deal with it, so my conclusion is that you should be fine for your requirements.

Comments

0

It seems to be ok, as ConcurrentHashMap deals with thread safety, according to the spec http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ConcurrentHashMap.html

Comments

0

Your code isn't threadsafe, not because of byte stuff but because of the way you use ConcurrentHashMap.

For adding items in the map, you should use putIfAbsent() over put(). PutIfAbsent() is equivalent to

   if (!map.containsKey(key)){
      return map.put(key, value);
   }
   else {
       return map.get(key);
   }

(probably with proper synchronized block or keyword)

If you just use put(), there are chance that you'll override existing value with new value in multi-thread environment.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.