简体   繁体   中英

Synchronized (Hash)Map of Singletons

Description below the code...

// Singleton
public static final Map<String, Account> SHARED_ACCOUNT_HASHMAP =
        Collections.synchronizedMap(new HashMap<>());


public init(String[] credentials) {

    Account account = null;     

    String uniqueID = uniqueAccountIdentifier(credentials);

    if (SHARED_ACCOUNT_HASHMAP.containsKey(uniqueID)) {
        account = SHARED_ACCOUNT_HASHMAP.get(uniqueID);
        log("...retrieved Shared Account object: %s", uniqueID);
    }

    // create the Account object (if necessary)
    if (account == null) {
        account = new Account(credentials);

        // Store it in the SHARED_ACCOUNT_HASHMAP
        SHARED_ACCOUNT_HASHMAP.put(uniqueID, account);
        log("...created Account object: %s",uniqueID);

    }

}

What I want to achieve

  • There are multiple Threads accessing this Singleton HashMap
  • The goal of this HashMap is to only allow the creation of ONE Account per uniqueID
  • The account later can be retrieved by various threads for Account operations
  • Each Thread has this init() method and runs it once.
  • So the first Thread that cannot find an existing uniqueID Account, creates a new one and places it in the HashMap. The next Thread finds that for the same uniqueID, there is an Account object already - so retrieves it for its own use later

My problem...

  • How can I get the other Threads (second, third, etc.) to wait while the first Thread is inserting a new Account object?
  • to phrase it another way, there should never be 2 threads ever that receive a value of null when reading the HashMap for the same uniqueID key. The first thread may receive a value of null, but the second should retrieve the Account object that the first placed there.

According to the docs for synchronizedMap()

Returns a synchronized (thread-safe) map backed by the specified map. In order to guarantee serial access, it is critical that all access to the backing map is accomplished through the returned map.

It is imperative that the user manually synchronize on the returned map when iterating over any of its collection views

In other words you still need to have synchronized access to SHARED_ACCOUNT_HASHMAP :

public init(String[] credentials) {
    Account account = null;     
    String uniqueID = uniqueAccountIdentifier(credentials);

    synchronized (SHARED_ACCOUNT_HASHMAP) {
        if (SHARED_ACCOUNT_HASHMAP.containsKey(uniqueID)) {
            account = SHARED_ACCOUNT_HASHMAP.get(uniqueID);
            log("...retrieved Shared Account object: %s", uniqueID);
        }

        // create the Account object (if necessary)
        if (account == null) {
            account = new Account(credentials);

            // Store it in the SHARED_ACCOUNT_HASHMAP
            SHARED_ACCOUNT_HASHMAP.put(uniqueID, account);
            log("...created Account object: %s",uniqueID);
        }
    }
}

Consider using ReadWriteLock if you have multiple readers/writers (see ReadWriteLock example ).

Generally the ConcurrentHashMap performs better than the sinchronized hash map you are using.

In the following code I can feel smell of race condition check-then-act as you are trying to perform two operations on the synchronised map ( containsKey and get ):

if (SHARED_ACCOUNT_HASHMAP.containsKey(uniqueID)) {
        account = SHARED_ACCOUNT_HASHMAP.get(uniqueID);
        log("...retrieved Shared Account object: %s", uniqueID);
 }

So to avoid race condition you need to synchronize over this map as:

synchronized (synchronizedMap) {
  if (SHARED_ACCOUNT_HASHMAP.containsKey(uniqueID)) {
            account = SHARED_ACCOUNT_HASHMAP.get(uniqueID);
            log("...retrieved Shared Account object: %s", uniqueID);
     }
  // rest of the code.      
}

Actually the synchronizedMap can protect itself against internal race conditions that could corrupt the map data but for external conditions (like above) you need to take care of that. If you feel you are using synchronized block at many places you can also think of using a regular map along with synchronized blocks. You will find this question also useful.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM