这个在 ConcurrentHashMap 上的操作是线程安全的吗?

Is it Thread-safe on this operating on ConcurrentHashMap?

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();
}

投票方法是否完全线程安全? VoteItem.getNum() returns 一个原子整数?或者有没有更好的实现方式?

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

但是有更常见的方法可以使用 putIfAbsent() 的结果来实现它,如果给定键存在 return 的现有值:

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

这也处理并发删除的可能性。与您的代码相反,可以在 putIfAbsent()get() 之间执行并发删除,从而导致 NPE,这里不会发生这种情况。

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

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

在结果上调用 getNum() 是可能的,因为与 putIfAbsent() 相比,如果值在插入之前不存在,returns 为 null,它 returns刚刚计算出的值。