代码之家  ›  专栏  ›  技术社区  ›  Yu Sun

在ConcurrentHashMap上执行此操作是否线程安全?

  •  2
  • Yu Sun  · 技术社区  · 8 年前
    private final ConcurrentHashMap<Float, VoteItem> datum = new ConcurrentHashMap<>();
    
    public void vote(float graduation) {
        datum.putIfAbsent(graduation, new VoteItem(graduation, new AtomicInteger(0)));
        datum.get(graduation).getNum().incrementAndGet();
    }
    

    方法投票是完全线程安全的吗?投票项目。getNum()返回一个AtomicInteger?或者是否有更好的方法来实现它?

    1 回复  |  直到 8 年前
        1
  •  4
  •   Alex Salauyou    8 年前

    如果 VoteItem#getNum() 是线程安全的,例如返回final属性, 没有在并行线程中执行删除,您的代码也是线程安全的,因为没有机会 putIfAbsent() 覆盖现有条目,因此没有机会 get() 返回被覆盖的条目。

    但有更常见的方法可以使用 putIfAbsent() ,如果给定键存在现有值,则返回该值:

    public void vote(float graduation) {
        VoteItem i = datum.putIfAbsent(graduation, new VoteItem(graduation, new AtomicInteger(1)));
        if (i != null)
            i.getNum().incrementAndGet();
    }
    

    这也处理了并发删除的可能性。与您的代码相反,可以在 putIfAbsent() 获取() 因此导致NPE,这里不可能发生这种情况。

    并考虑使用 computeIfAbsent() 而不是 putIfAbsent() 为了避免不必要的 VoteItem 创作:

    public void vote(float graduation) {
        datum.computeIfAbsent(graduation, g -> new VoteItem(g, new AtomicInteger(0)))
             .getNum()
             .incrementAndGet();
    }
    

    使命感 getNum() 结果是可能的,因为与 putIfAbsent() ,如果值在插入之前不存在,则返回null,它只返回计算值。