通过实现更好的性能来同步方法?

时间:2019-05-06 20:47:58

标签: java multithreading thread-safety

我有一个由多核计算机上的多个线程调用的类。我想使其线程安全。

add方法将由多个线程调用。如果存在键,则只需将当前值附加到新值,否则只需将键和值放在地图中即可。

现在要使其变得线程安全,我打算同步add方法,但是它将破坏性能。有什么更好的方法可以使我们在不同步add方法的情况下获得更好的性能?

class Test {
  private final Map<Integer, Integer> map = new ConcurrentHashMap<>();

  public void add(int key, int value) {
    if (map.containsKey(key)) {
      int val = map.get(key);
      map.put(key, val + value);
      return;
    }
    map.put(key, value);
  }

  public Object getResult() {
    return map.toString();
  }
}

3 个答案:

答案 0 :(得分:4)

  

但它会破坏性能

它可能不会破坏性能。它将减少一些碰撞,如果碰撞率较高,则会进一步降低。

  

是否有更好的方法可以达到更好的性能?

是的,请使用merge()(Java 8+)。引用javadoc:

  

如果指定的键尚未与某个值关联或与null关联,请将其与给定的非null值关联。否则,用给定的重映射函数的结果替换关联的值,或者如果结果为null则将其删除。

示例:

public void add(int key, int value) {
    map.merge(key, value, (a, b) -> a + b);
}

或者使用对sum(int a, int b)方法引用而不是 lambda表达式

public void add(int key, int value) {
    map.merge(key, value, Integer::sum);
}

答案 1 :(得分:3)

使用merge

class Test {
    final Map<Integer, Integer> map = new ConcurrentHashMap<>();

    public void add(int key, int value) {
        map.merge(key, value, Integer::sum);
    }

    public Object getResult() {
        return map.toString();
    }
}

如果绝对不能使用同步(或者绝对不能显式锁定),则为Java 7解决方案:

class Test {
    final Map<Integer, AtomicInteger> map = new ConcurrentHashMap<>();

    public void add(int key, int value) {
        get(key).addAndGet(value);
    }

    private AtomicInteger get(int key) {
        AtomicInteger current = map.get(key);

        if (current == null) {
            AtomicInteger ai = new AtomicInteger();

            current = map.putIfAbsent(key, ai);

            if (current == null) {
                current = ai;
            }
        }

        return current;
    }

    public Object getResult() {
        return map.toString();
    }
}

答案 2 :(得分:1)

'false'仅在您执行昂贵的操作并持有锁时才会导致瓶颈。
在您的情况下,通过添加synchronized可以做到:
1.检查哈希图是否存在键
2.获取映射到该键的值
3.进行加法运算,然后将结果放回哈希图中。

所有这些操作都是非常便宜的synchronized,除非您为整数键使用一些奇怪的模式,否则由于碰撞而导致性能下降的可能性很小。

我建议您是否不能使用O(1)来回答其他问题,只能使用merge。您应该只在关键的热路径中才对性能有足够的了解,并且在您实际分析出那里存在问题之后