java并发写入集合,然后读取-结果不一致

java并发写入集合,然后读取-结果不一致,java,concurrency,Java,Concurrency,我从中了解到,Set有几种不同的线程安全选项。在我的应用程序中,我有10个线程同时向一个集合添加内容(不必设置,但更好)。在所有线程完成后,我需要遍历集合 我了解到ConcurrentSkipListSet和Collections.newSetFromMap(new ConcurrentHashMap())都有不一致的批处理操作(addAll、removeAll等)和迭代器。我的实验也证实了这一点。当我使用ConcurrentSkipListSet时,在所有线程相加后,读数有点随机。我随机得到不

我从中了解到,Set有几种不同的线程安全选项。在我的应用程序中,我有10个线程同时向一个集合添加内容(不必设置,但更好)。在所有线程完成后,我需要遍历集合

我了解到ConcurrentSkipListSet和Collections.newSetFromMap(new ConcurrentHashMap())都有不一致的批处理操作(addAll、removeAll等)和迭代器。我的实验也证实了这一点。当我使用ConcurrentSkipListSet时,在所有线程相加后,读数有点随机。我随机得到不同大小的集合

然后我尝试了Collections.synchronizedSet(newhashset()),我认为它应该是线程安全的,因为它同时阻止了多个写访问。 然而,它似乎有同样的不一致的阅读问题。我仍然随机地在结果集中得到不同的大小

我应该如何确保读数一致?如前所述,我不必使用Set。我可以使用列表或其他方法,只要有避免重复添加的方法

显示代码有点困难,因为它是一个非常大的包的一部分。但总的来说是这样的

public class MyRecursiveTask extends RecursiveTask<Integer> {
    private List<String> tasks; 
    protected ConcurrentSkipListSet<String> dictionary;
    public MyRecursiveTask(ConcurrentSkipListSet<String> dictionary,
                           List<String> tasks){
        this.dictionary=dictionary;
        this.tasks=tasks;
    }

    protected Integer compute() {
        if (this.tasks.size() > 100) {
            List<RecursiveFeatureExtractor> subtasks =
                new ArrayList<>();
            subtasks.addAll(createSubtasks());
            int count=0;
            for (MyRecursiveTask subtask : subtasks)
                subtask.fork();
            for (MyRecursiveTask subtask : subtasks)
                count+=subtask.join();
            return count;
        } else {
            int count=0;
            for (File task: tasks) {
                    // code to process task
                 String outcome = [method to do some task]
                 dictionary.add(outcome);
                 count++;
            }
            return count;
        }
    }

    private List<MyRecursiveTask> createSubtasks() {
        List<MyRecursiveTask> subtasks =
            new ArrayList<>();

        int total = tasks.size() / 2;
        List<File> tasks1= new ArrayList<>();
        for (int i = 0; i < total; i++)
            tasks1.add(tasks.get(i));
        MyRecursiveTask subtask1 = new MyRecursiveTask(
            dictionary, tasks1);

        List<File> tasks2= new ArrayList<>();
        for (int i = total; i < tasks.size(); i++)
            tasks2.add(tasks.get(i));
        MyRecursiveTask subtask2 = new MyRecursiveTask(
            dictionary, tasks2);

        subtasks.add(subtask1);
        subtasks.add(subtask2);

        return subtasks;
    }
}
公共类MyRecursiveTask扩展了RecursiveTask{
私有列表任务;
受保护的ConcurrentSkipListSet字典;
公共MyRecursiveTask(ConcurrentSkipListSet字典,
列出任务){
这个。字典=字典;
这个。任务=任务;
}
受保护整数计算(){
if(this.tasks.size()>100){
列出子任务=
新的ArrayList();
addAll(createSubtasks());
整数计数=0;
for(MyRecursiveTask子任务:子任务)
子任务fork();
for(MyRecursiveTask子任务:子任务)
count+=子任务。join();
返回计数;
}否则{
整数计数=0;
用于(文件任务:任务){
//处理任务的代码
字符串结果=[执行某些任务的方法]
添加(结果);
计数++;
}
返回计数;
}
}
私有列表创建子任务(){
列出子任务=
新的ArrayList();
int total=tasks.size()/2;
List tasks1=new ArrayList();
对于(int i=0;i
然后,创建此类线程工作线程列表的代码:

....
List<String> allTasks = new ArrayList<String>(100000);
....
//code to fill in "allTasks"
....

ConcurrentSkipListSet<String> dictionary = new ConcurrentSkipListSet<>();
//I also tried "dictionary = Collections.Collections.synchronizedSet(new 
//HashSet<>())" and changed other bits of code accordingly. 
ForkJoinPool forkJoinPool = new ForkJoinPool(10);
MyRecursiveTask mrt = new MyRecursiveTask (dictionary,
            );
int total= forkJoinPool.invoke(mrt);
System.out.println(dictionary.size()); //this value is a bit random. If real     
//size should be 999, when I run the code once i may get 989; second i may 
//get 999; third I may get 990 etc....
。。。。
列出所有任务=新建阵列列表(100000);
....
//填写“所有任务”的代码
....
ConcurrentSkipListSet字典=新的ConcurrentSkipListSet();
//我还尝试了“dictionary=Collections.Collections.synchronizedSet(新建)”
//HashSet())并相应地更改了其他代码位。
ForkJoinPool ForkJoinPool=新的ForkJoinPool(10);
MyRecursiveTask mrt=新建MyRecursiveTask(字典,
);
int total=forkJoinPool.invoke(mrt);
System.out.println(dictionary.size())//这个值有点随机。如果真的
//大小应该是999,当我运行代码一次,我可能会得到989;第二,我可以
//获得999;第三,我可能会得到990等。。。。

谢谢

没有看到代码,很难说是什么错。我猜读取结果的线程运行得太早,而一些线程仍在编写。使用Thread.join等待写入程序。Collections.synchronizedSet肯定是线程安全的

从以下角度考虑这一点:

用户必须手动同步返回的数据 在对其进行迭代时设置:

不遵循此建议可能会导致不确定性行为。返回的集将被删除 如果指定的集可序列化,则可序列化


请给我们一段代码,显示您的问题,以及预期和实际输出。您可能将数据竞争与一般竞争条件混淆了。并发类只会使您免于前者,但您的代码会受到后者的影响。只是添加了一些代码。我怀疑比赛情况。对并发集的唯一操作是向其中添加内容,并且对集中的元素没有其他检查。
   Set s = Collections.synchronizedSet(new HashSet());
       ...   synchronized (s) {
       Iterator i = s.iterator(); // Must be in the synchronized block
       while (i.hasNext())
           foo(i.next());   }