假设我有一个线程安全的集合,我将以下列方式填充:
Set set = new HashSet();
for (Map map : maps) {
set.addAll(doSomeExpensiveProcessing(map.keySet()));
}
同时执行此操作的最佳方式是什么? (即每个地图会同时将其键添加到集合中。
编辑 - 我知道HashSet不是线程安全的,但就我所关注的问题而言,这不属于问题的范围。
EDIT2 - 正确指出,对于这种特殊情况,并发性不会带来巨大的好处,但会有其他步骤,我现在已经将其包含在代码示例中。
答案 0 :(得分:2)
这应该有效:
// NB - Be sure to use a concurrent form of Set here.
Set set = new HashSet();
ArrayList<Map> maps = new ArrayList<>();
public void test() {
for (final Map map : maps) {
new Thread(new Runnable() {
@Override
public void run() {
set.addAll(map.keySet());
}
}).start();
}
}
我意识到你对并发HashSet
的实现不感兴趣,但为了完整性,我想提一下选项。
如果您的对象实施ConcurrentSkipListSet
,您可以考虑Comparable
,或者Collections.newSetFromMap(new ConcurrentHashMap<Object,Boolean>())
也可以。
答案 1 :(得分:1)
虽然@OldCurmudgeon有一个很好的基本方法,但在更严肃的代码中,你可能想要制作一个Callable
来执行昂贵的密钥处理,并返回一个新的Collection
。这可以与Executor和/或CompletionService结合使用。你最后甚至不需要并发收集。
public class DoesExpensiveProcessing implements Callable<Set<String>> {
final Set<String> inKeys;
public DoesExpensiveProcessing(Set<String> keys) {
this.inKeys = keys; // make a defensive copy if required...
}
public Set<String> call() {
// do expensive processing on inKeys and returns a Set of Strings
}
}
此时您甚至不需要并行收集
List<DoesExpensiveProcessing> doInParallel = new ArrayList<DoesExpensiveProcessing>();
for (Map map : maps) {
doInParallel.add(new DoesExpensiveProcessing(map.keySet()));
}
Set theResultingSet = new HashSet<String>();
List<Future<Set<String>>> futures = someExecutorService.invokeAll(doInParallel);
for (Future<Set<String>> f : futures) {
theResultingSet.addAll(f.get());
}
答案 2 :(得分:0)
这样就不会同时发生,但至少是线程安全的:
Set set = Collections.synchronizedSet(new HashSet());
...
// in some other threads:
for (Map map : maps) {
set.addAll(map.keySet());
}
或者您更喜欢以下内容:
ConcurrentMap<Object, Boolean> set = new ConcurrentHashMap<Object, Boolean>();
...
// in some other threads:
for (Map map : maps) {
for (Object o : map.keySet()) {
set.putIfAbsent(o, true);
}
}