robinkc
robinkc

Reputation: 1348

Do we need to synchronize java HashMap gets if there is only one writer thread and no structural modifications to the map are done

In my application I need to maintain an in-memory HashMap that stores list of userIds along with their score.

There is one Writer Thread that updates the score of the user based on business logic. And many Reader Threads read the score of users from this map, ie, map.get(userId).

The list of userIds is static, ie, no new users are added to the map.

As per JavaDoc If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally.

I am not making any structural changes (no additions / deletions). Do I need to use ConcurrentHashMap or any other Synchronization construct for such a use case?

Upvotes: 2

Views: 288

Answers (1)

Marko Topolnik
Marko Topolnik

Reputation: 200206

Your map.put operation will just update the value field of HashMap$Node. This is safe with respect to HashMap's structural consistency, but there is nevertheless a data race on the value field. If your value type is a simple value class like Integer, Long, or Double, it is safe to dereference it even over a data race, but there is no guarantee that the consumer will ever see a score updated.

A clean solution to this is replacing your e.g. Long with AtomicLong as the value type. Then your map will never be updated, only its values will be mutated in a thread-safe manner.

This is the outline of solution:

  1. Safely publish the map:

    volatile Map<Player, AtomicLong> scores;
    
    void publishScores() {
       scores = unmodifiableMap(createScoresMap());
    }
    
  2. Update a score:

    void updateScore(Player p, long score) {
        map.get(p).set(score);
    }
    
  3. Read a score:

    long getScore(Player p) {
       return map.get(p).get();
    }
    

Upvotes: 6

Related Questions