java - 同时检查重复项+将项目添加到Java中的列表/集合中

标签 java multithreading concurrency set thread-safety

我有一些代码,通过启动多个线程并以给定的速率使用指定的事务命中服务来针对 Web 服务运行负载测试。事务从服务中检索值列表,然后检查值列表以查看它们是否存在于集合中,如果不存在则添加它们,如果存在则使事务失败(我知道单独检查不是必要的,并且可以检查添加的返回值 - 这就是现在编写代码的方式)。

但是查看代码,它不是线程安全的。正在检查/添加到的集合是基本的哈希集。当前的代码还会为每个事务增加常规 hashMap 中的一个值,因此看起来这段代码从一开始就在线程安全方面被搞乱了。

我相信我使用基于 ConcurrentHashMap 的解决方案解决了 Map 增量问题:Atomically incrementing counters stored in ConcurrentHashMap ,但我不确定以线程安全的方式处理 Set 上的重复检查/修改的最佳方法。

最初我考虑使用 CopyOnWriteArraySet,但由于预期的情况是不重复,读取会像写入一样频繁发生,所以它看起来并不理想。我现在考虑的解决方案是使用 newKeySet()/KeySet(defaultVal) 在 ConcurrentHashMap 上使用 Set 'view',如下所述:https://javarevisited.blogspot.com/2017/08/how-to-create-thread-safe-concurrent-hashset-in-java-8.html

如果我使用此解决方案通过仅添加值并检查 bool 返回类型来检查重复项,这会以线程安全的方式实现我想要的吗?我主要担心的是,检测任何重复项很重要。我不希望发生的是两个线程尝试同时添加,并且两个添加都返回 true,因为当它们尝试添加时该值不存在,并且从服务接收到的重复值未被检测到。为此,我想也许我应该使用列表并通过转换为集合并检查大小来检查最后是否有重复项?不过,最好至少尝试在事务期间检测重复项,如果检测到则失败。有时得到假阴性并仍然通过交易(如果我们最终能够检测到它)是可以的,但我认为,当我们可以时检查/失败交易仍然很有值(value)。

任何建议表示赞赏 - 谢谢!

最佳答案

I believe I solved the Map increment issue using ConcurrentHashMap based solution here: Atomically incrementing counters stored in ConcurrentHashMap, but I'm not sure the best way to handle the duplicate check/modification on the Set in a thread-safe way.

是的,您当然可以在解决方案中使用ConcurrentHashMap

If I use this solution checking for duplicates by just adding the value and checking the bool return type, will this achieve what I want in a thread-safe way?

是的。 ConcurrentHashMap 是一个完全可重入的类,因此如果两个线程在同一时刻对同一键执行 put(...),其中一个将获胜并返回null 作为现有键,另一个将替换该键并返回您可以测试的键的先前值。它专为高性能多线程应用程序而设计。您还可以执行 putIfAbsent(...) 在这种情况下,第二个线程(以及任何其他线程)将返回映射中已有的值。如果您使用键集包装器来提供 Set 机制,这也将起作用。

对于所有同步类,当您多次调用该类时,您需要小心代码中的竞争条件。例如,如下所示的模式是一个糟糕的模式,因为由于多次调用并发映射而存在竞争条件:

// terrible pattern which creates a race condition
if (!concurrentMap.containsKey(key)) {
   concurrentMap.put(key, value);
}

这就是为什么 ConcurrentMap 有许多原子操作来帮助实现这一点:

  • V putIfAbsent(K key, V value); -- 如果 key 不存在,则将其放入映射中
  • boolean remove(K key, V value); -- 如果键有值,则从 map 中删除该键
  • boolean Replace(K key, V oldValue, V newValue); -- 仅当键已有旧值时才用新值替换键
  • V Replace(K key, V value); -- 仅当键已存在于映射中时才替换与键关联的值

所有这些方法都需要对同步映射进行多次非原子调用才能从外部实现,这会引入竞争条件。

My main concern is that it is important that I DO detect any duplicates. What I don't want to happen is two threads try to add at the same time, and both adds return true...

如上所述,这种情况不会发生。 2 个 put 之一将返回 null,另一个应被视为重复项。

For that purpose I thought maybe I should use a List and check for duplicates at the end by converting to a set and checking size?

该列表是不必要的,而且很难得到正确的结果。

关于java - 同时检查重复项+将项目添加到Java中的列表/集合中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50842044/

相关文章:

java - 我怎样才能得到一个数组的迭代器?

c# - 使用多线程在 SQLite 数据库中插入记录的性能问题

java - 单独列表上的 ConcurrentModificationException 不清楚

multithreading - 这段代码线程安全吗

java - 并行计算

go - 如何同步用于并发初始化 slice 的 go 例程?

java - 如何为一个类实例化更多的CDI bean?

java - "variable is accessed from within inner class needs to be declared final"错误

java - 外部目录中的 JSP 文件

c - 当按值传递静态变量时会导致 c 中的竞争条件