您提出的方法不是线程安全的,因为初始
hashTable.get()操作(通过该操作您将获得要在其上进行同步的对象)相对于
put()具有相同键关联值的其他线程而言本身并不同步。此外,您的代码不考虑将新值添加到映射或从映射中删除键的可能性(所谓的“结构修改”)。如果有可能发生这种情况,无论键如何,那么这些动作都必须相对于对地图的
所有 其他访问进行同步。
没错,但是,这
ConcurrentHashMap也不能解决这些问题。就其提供的各个操作而言,它是线程安全的,其中包括一些
Map自身未定义的操作,但是必须作为同步单元执行的
一系列 操作仍需要通过同步来保护。
我建议使用一种稍微不同的方法:使用一个可变的
ConcurrentHashMapwith
AtomicLong,作为您的值类型,而不是
Long:
ConcurrentHashMap<String, AtomicLong> map;
然后,即使您不确定该键在地图中是否已有条目,也要更新键的值,请执行以下操作:
AtomicLong value = map.putIfAbsent(key, new AtomicLong(0));long updatedValue = value.incrementAndGet();
在
putIfAbsent()确保值对象不冲突的把操作重挫。使用
AtomicLongno避免了多个操作被共同同步的需要,因为只需要一次映射访问-
检索到的值由访问它的所有线程共享,并且它本身可以原子更新,而无需进一步访问映射。
如果可以确定该映射已经具有给定键的映射,则只需执行以下操作:
AtomicLong value = map.get(key);long updatedValue = value.incrementAndGet();
一种或另一种方式,我认为这是您所描述和隐含的操作中可以做的最好的事情。
更新:
您甚至可以考虑将以下两种方法结合起来:
AtomicLong value = map.get(key);if (value == null) { value = map.putIfAbsent(key, new AtomicLong(0));}long updatedValue = value.incrementAndGet();假定相对很少有给定密钥没有映射,并且
AtomicLong在这种情况下避免创建新密钥。如果未找到映射,则必须第二次访问该映射以确保存在映射并获取相应的值,但是在这里,
putIfAbsent()如果要避免同步,我们仍然需要,因为两个线程可能都尝试在大约同一时间为同一键添加一个映射。当需要添加新条目时,这样做的成本更高,但是平均而言,其成本可能比我的第一个建议要低。但是,与任何性能问题一样,必须进行测试。



