我有一些代码正在通过启动多个线程并以给定速率使用指定事务命中服务来对Web服务运行负载测试。事务从服务中检索值列表,然后检查值列表以查看它们是否存在于集合中,如果它们不存在则会添加它们,如果它们存在则会添加它们(我知道separte检查)没有必要,可以检查添加的返回值 - 这就是现在代码的编写方式。)
然而,查看代码,它不是线程安全的。正在检查/添加的集是一个基本的HashSet。当前代码还为每个转换增加了常规hashMap中的值 - 所以看起来这个代码在线程安全时从一开始就被用到了。
我相信我在这里使用基于ConcurrentHashMap的解决方案解决了Map增量问题:Atomically incrementing counters stored in ConcurrentHashMap,但我不确定以线程安全的方式处理Set上的重复检查/修改的最佳方法
最初我考虑过使用CopyOnWriteArraySet,但由于预期的情况是没有重复,因此读取会像写入一样频繁发生,因此它看起来并不理想。我现在考虑的解决方案是使用Set' view'在ConcurrentHashMap上使用newKeySet()/ KeySet(defaultVal),如下所述:https://javarevisited.blogspot.com/2017/08/how-to-create-thread-safe-concurrent-hashset-in-java-8.html
如果我通过添加值并检查bool返回类型来使用此解决方案来检查重复项,那么这将以线程安全的方式实现我想要的吗?我主要担心的是,我必须检测任何重复项。我不希望发生的是两个线程同时尝试添加,并且两个添加都返回true,因为当他们尝试添加时,值不存在,并且从服务接收的重复值未被检测到。为此,我想也许我应该使用List并通过转换为set并检查大小来检查最后的重复项?然而,至少尝试在交易期间检测重复并且如果检测到则失败仍然是优选的。如果我们能够在最后检测到它,那么有时会得到一个假的否定并仍然通过交易,但我认为当我们能够检查/失败的交易仍然有价值。
感谢任何建议 - 谢谢!
答案 0 :(得分:0)
我认为类似ConcurrentHashSet的设置是你最好的朋友:
Set<Value> values = ConcurrentHashMap.newKeySet();
该集由ConcurrentHashMap
支持,因此您的代码将受益于ConcurrentHashMap
答案 1 :(得分:0)
只是一点建议 - 如果您的Transaction对象(或您放入Set中的任何对象)具有正确的equals方法实现,则无需在Set中检查重复项。
Set始终只有唯一值。 如果你仍然需要知道对象已经在set use contains方法。
然后有多种方法可以满足您的需求。
您可以使用ConcurrentHashMap
代替Set
,只需将对象作为键即可。你有一个keySet,你可以使用它。值可以是任何值(例如,相同的对象)。当然你也可以使用valueSet。
您可以使用BlockingQueue
之一(例如LinkedBlockingQueue
)实现首先从不同的线程收集事务,然后在完成所有线程后应用您想要的任何逻辑
还有很多其他方法......
答案 2 :(得分:0)
我相信我在这里使用了基于
ConcurrentHashMap
的解决方案解决了Map增量问题:以原子方式递增存储在ConcurrentHashMap
中的计数器,但是我不确定最好的方式来处理以线程安全的方式进行设置。
是的,您当然可以在解决方案中使用ConcurrentHashMap
。
如果我使用此解决方案通过仅添加值并检查bool返回类型来检查重复项,是否可以通过线程安全的方式实现我想要的目标?
是的。 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);
-如果还不存在,请将密钥放入地图boolean remove(K key, V value);
-如果该键具有值,则将其从地图中删除boolean replace(K key, V oldValue, V newValue);
-仅当密钥已经具有旧值时才将其替换为新值V replace(K key, V value);
-仅在地图中已经存在键的情况下替换与键关联的值所有这些方法都需要对同步地图进行多次非原子调用才能从外部实现,这会引入竞争条件。
我主要担心的是,我必须检测任何重复项,这一点很重要。我不想发生的是两个线程试图同时添加,并且两个都添加返回true ...
如上所述,这不会发生。 2个看跌期权之一将返回null
,而另一个应视为重复。
为此,我想也许我应该使用一个List并通过转换为set并检查大小来检查是否有重复项?
该列表是不必要的,而且很难弄清。