单身人士的同步(哈希)地图

时间:2015-10-20 05:11:51

标签: java multithreading collections hashmap synchronization

代码下面的描述...

// 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);

    }

}

我想要实现的目标

  • 有多个线程访问此Singleton HashMap
  • 此HashMap的目标是仅允许为每个uniqueID创建一个帐户
  • 稍后可以通过各种线程检索帐户操作
  • 每个线程都有这个init()方法并运行一次。
  • 因此第一个找不到现有uniqueID帐户的Thread会创建一个新的并将其放在HashMap中。下一个Thread发现对于相同的uniqueID,已经有一个Account对象 - 所以稍后检索它以供自己使用

我的问题......

  • 当第一个Thread插入新的Account对象时,如何让其他线程(第二个,第三个等)等待?
  • 以另一种方式表达它,在读取相同uniqueID键的HashMap时,永远不会有2个线程接收到null值。第一个线程可能会收到null值,但第二个线程应该检索第一个放置在那里的Account对象。

3 个答案:

答案 0 :(得分:1)

根据synchronizedMap()

的文档
  

返回由指定映射支持的同步(线程安全)映射。为了保证串行访问,必须通过返回的映射完成对支持映射的所有访问。

     

当迭代任何集合视图时,用户必须手动同步返回的地图

换句话说,您仍然需要synchronized 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); } } }

{{1}}

答案 1 :(得分:1)

如果您有多个读者/作者,请考虑使用ReadWriteLock(参见ReadWriteLock example)。

通常,ConcurrentHashMap的性能优于您正在使用的同步哈希映射。

答案 2 :(得分:1)

在以下代码中,当您尝试在同步地图(check-then-actcontainsKey)上执行两项操作时,我会感觉到种族条件get的气味:

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

因此,为了避免竞争条件,您需要在此地图上进行同步:

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

实际上synchronizedMap可以保护自己免受可能破坏地图数据的内部竞争条件的影响,但是对于外部条件(如上所述),您需要注意这一点。如果您觉得在许多地方使用synchronized块,您还可以考虑使用常规地图和同步块。你会发现这个question也很有用。