我有一个由多核计算机上的多个线程调用的类。我想使其线程安全。
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();
}
}
答案 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
。您应该只在关键的热路径中才对性能有足够的了解,并且在您实际分析出那里存在问题之后