线程安全数据结构以检查是否存在,如果不存在则写入

时间:2018-06-23 14:37:07

标签: java concurrency java.util.concurrent

我想解析一长串带有重复项的字符串,并将每个唯一字符串仅一次保存到一个数组中。在多线程方法中,线程将检查共享数据结构是否存在,如果不存在则进行写入。

我忘记了什么数据结构适合于此。 Java.util中的任何内容都可以,高性能的第三方库也可以。

2 个答案:

答案 0 :(得分:3)

java.util 包中的集合类不是线程安全的,以便在单线程应用程序中提供最佳性能。 (Vector和Hashtable是例外)

有几种方法可以实现所需的线程安全性。

同步包装器 Set<String> safeSet = Collections.synchronizedSet(new HashSet<>());

这会将对基础集的所有调用包装在一个同步块中,从而锁定该对象。但是,这意味着当线程迭代集合中的元素时,所有其他集合的方法都将阻塞,从而导致其他线程不得不等待。

java.util.concurrent软件包

Java 5引入了并发集合,它们提供了比同步包装更好的性能。

有不同的风格:写时复制,比较并交换和并发集合。

并发集合使用特殊的Lock,它比同步更灵活。

因此对于您正在执行的操作,如果HashSet是单线程的,则可能是一个很好的匹配。在并发包中,您可以使用ConcurrentHashMap。

它看起来像这样:

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

...

 private static final Object PRESENT = new Object();
 Map<String, Object> seenStrings = new ConcurrentHashMap<>();



for ( String aString : stringList ) {
    if ( seenStrings.containsKey(aString) ) {
        // Already there
    } else {
        // Not seen yet
        seenStrings.put(aString, PRESENT);
    }
}

更新 安迪的评论很不错,如果您已经看过某项,或者您还没有看过,我不确定您想做什么。

您可以执行此操作以确保自动执行检查和插入

if (seenStrings.put(aString, PRESENT) == null) {
       // Not seen yet
} 

更新,在Java 8+中,您可以创建一个由指定地图支持的集合。有效地是ConcurrentHashSet。

Set<String> seenStrings = Collections.newSetFromMap(new ConcurrentHashMap<>());
for (String aString : stringList) {
    if (seenStrings.add(aString)) {               
            // Not seen yet
    }
}

答案 1 :(得分:1)

您可以为此目的使用CopyOnWriteArrayListConcurrentLinkedQueue。但是,如果您有很多写CopyOnWrite的方法,则将花费很多。

如果要删除重复项,请考虑使用CopyOnWriteArraySet