Concurrent HashMap 在多线程环境中表现不安全
Concurrent HashMap 在多线程环境中表现不安全
我有一个CHM\n
private ConcurrentHashMappR = new ConcurrentHashMap<>();
\n我有一个方法,它会将其值递增\n
public void incrementPR(int count){ Integer value = this.pR.get(count); if(value == null){ this.pR.put(count,1); } else { this.pR.put(count,value+1); } }
\n这个方法是通过jmeter从一个端点调用的,如果我同时发送500个请求,哈希映射保留的值不是500,而是437、430等,它的行为不是线程安全的,我们如何实现线程安全?
Concurrent HashMap not Behaving in thread Safe Manner(ConcurrentHashMap在多线程环境下不能保持线程安全的原因)
当一个类被描述为“线程安全”的时候,通常意味着该类将确保在并发调用其方法时有足够的排他性,以维护类的完整性和相关的不变量。
但这并不意味着另一个类在调用之间不能修改它。
在你的情况下,如果一个线程在调用this.pR.get(count);
之后,另一个线程在调用put
之前也调用了它,它们将在更新对象方面存在竞争,并且似乎“丢失”了计数。
你需要在调用incrementPR
时加上一些同步操作。
在这种情况下,简单明了的答案是:
public synchronized void incrementPR(int count){
//....
}
事实上,如果有同步操作,可能不需要使用同步映射。从提供的片段中无法确定这一点。
是的,我使用了同步,它运行得很好,但我希望有更好的方法,因为我觉得同步会对性能产生影响,并且虽然不明显,但当模型中包括其他变量时,它会反映出来。
在这种情况下,锁或同步哪个更好?
我提供了最简单的答案,但事实上,我建议使用锁。我认为在Java中,synchronized
是一种不好的设计,你应该显式地声明和使用Lock
对象。为每个对象提供一个监视器,并在此方法中膨胀(可能导致性能膨胀)是完全不必要的。在一个良好的实现中,你应该能够使用任何一种锁实现相同的性能,除非你需要尝试不同类型的锁,否则synchronized
很快就会失败。
在重复标记中提到的链接在没有任何锁或同步的情况下工作
哪一个?其中一个是错误的,它们都使用了某种形式的同步。隐式或显式。我为你提供了最简单的安全解决方案。过早地进行优化是万恶之源。在你认为它是一个瓶颈之前,你不需要为一个简单的解决方案而苦恼。
那个可以工作并且有文档保证。是否更好取决于程序的配置文件,特别是负载和争用情况。在某些情况下(许多实际情况),它可能不具有最佳性能。与LongAdder
相比,AtomicLong
可能更适合一些情况。
Concurrent HashMap not Behaving in thread Safe Manner(Concurrent HashMap在多线程中无法保持线程安全的问题)
在某些语句中,您打破了“获取当前值”、“增加它”和“将其保存回映射”的步骤。当这些语句并发运行时,你得到了奇怪的结果。您的失败与并发映射无关。您应该为您的代码使用一些并发控制机制(如信号量、锁等)。有关更多信息,请参考以下问题:
Java Concurrency Incrementing a Value
如果您对解决方案的效率(不使用锁机制)感兴趣,您可以使用AtomicInteger
:
AtomicInteger atomicInteger = new AtomicInteger(); // 当您想要添加您的数字时,请使用以下代码 atomicInteger.getAndAdd(1);
但是,如果这个语句的负载很重,使用AtomicLong
可能会成为一个瓶颈(因为它使用比较和交换的CPU指令)。在这种情况下,最好使用LongAdder
:
LongAdder longAdder = new LongAdder(); // 当您想要添加您的数字时,请使用以下代码 longAdder.increment()
你是在说同步吗?有没有其他更好的方法?
在预期输入为整数的映射中,只需将映射声明为Map<您的键类型,LongAdder>
,现在在需要增加值时使用map.get(myKey).increment()
即可。
如果没有键存在,它能以线程安全的方式插入键吗?
我假设您的键是指标,在这种情况下,您可以使用上述的映射来拥有许多线程安全的计数器:map.get("numberOfX").increment()
或map.get("numberOfY").increment()
等等。