Correct use of ConcurrentHashMap

10 篇文章 0 订阅

ConcurrentHashMap has been pitched as a simple alternative for HashMap, eliminating the need for a synchronized blocks. I had some simple event counting code that created count records on the fly. Although I could have used synchronized blocks for safety I used ConcurrentHashMap for this situation, partly for efficiency but mostly for the exercise. Going through this made me realize how carefully ConcurrentHashMap must be used for your code to work correctly and efficiently.

When using a HashMap, the standard idiom to add a value if it doesn’t exist is to use code that looks something like this:

synchronized (this) {
  Record rec = records.get(id);
  if (rec == null) {
      rec = new Record(id);
      records.put(id, rec);
  }
  return rec;
}

If you were to simply replace HashMap with ConcurrentHashMap and remove the synchronized keyword your code would be exposed to a race condition. If a new Record was put into the map just after the call to getreturned null the put operation would overwrite the value. You could addsynchronized back in but this defeats the purpose of using ConcurrentHashMap.

To safely create values on demand you must use putIfAbsent (and avoid making extra calls to get in the process).

First check to see if a value with the key already exists in the map and use this value if it does. Otherwise, create a new value for the map and add it with putIfAbsentputIfAbsent returns any existing value if there is one, otherwise null (this is why ConcurrentHashMap can’t contain null values).

private ConcurrentMap<String, Record> records =
     new ConcurrentHashMap<String, Record>();

private Record getOrCreate(String id) {
    Record rec = records.get(id);
    if (rec == null) {
        // record does not yet exist
        Record newRec = new Record(id);
        rec = records.putIfAbsent(id, newRec);
        if (rec == null) {
            // put succeeded, use new value
            rec = newRec;
        }
    }
    return rec;
}

If putIfAbsent does return a value, it’s the one that must be used. It may have already been used by other threads at this point. The new value created must be abandoned. Although it sounds wasteful this case should happen very infrequently.

I’ve seen other code on the net that ignores the return value ofputIfAbsent and makes another call to get at the end to figure out which value made it into the map (the new value created or a value from another thread). Although this will work it introduces an unnecessary lookup.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值