如何在Java中使用多个线程迭代一个Collection,其中没有两个线程迭代在Collection的同一部分?

时间:2015-05-05 13:22:45

标签: java multithreading collections iteration

我需要迭代一个大的ArrayList(~50,000个条目),我需要使用多个线程来相当快地完成这个。

但我需要每个线程从一个唯一索引开始,这样就不会有两个线程迭代在列表的同一部分上。 batchSize100,因此每个帖子都会从startIndex循环到startIndex + 100

有没有办法实现这个目标?请注意,我只在这里执行读取操作,没有写入。列表中的每个条目只是一个String,它实际上是一个SQL查询,然后我通过JDBC对数据库执行。

3 个答案:

答案 0 :(得分:5)

如果您只打算阅读List,而不是改变它,则只需定义Runnable即可将ListstartIndex作为构造函数参数。只要没有线程同时修改ArrayList(即使是相同的索引),也没有危险。

为安全起见,请务必将ArrayList打包到Collections.unmodifiableList()并将 List 传递给Runnable。这样您就可以确信线程不会修改后备ArrayList

或者,您可以在主线程中构建子列表(使用List.subList()),这样您就不需要将startIndex传递给每个线程。但是,在执行此操作之前,您仍希望使子列表不可修改。一个中的六个,另一个中的六个。

更好的方法是使用Guava' s ImmutableList;它自然是线程安全的。

Java 8中也有parallel streams,但要注意这个解决方案;他们很强大,但很容易出错。

答案 1 :(得分:1)

如果您使用Java 8,请查看post "/logout" => "devise/sessions#destroy", :as => :destroy_user_session end

对于Java 7,在线程外部使用list.stream().parallel()将工作拆分为多个部分。然后,线程应该只在这样的子列表上运行。对于大多数列表,subList()是一种非常有效的操作,不会复制数据。如果修改了支持列表,那么您将获得subList()

在将数据提供给线程时,我建议查看ConcurrentModificationException API和Executor。只需将所有工件放入队列中,让执行人员解决所有问题。

答案 2 :(得分:0)

有一个原子变量:

int nextBatch = 0;

每次线程包含一个新批次时增加它:

public synchronized int getNextBatch() {
    nextBatch += batchSize;
    if(nextBatch >= arraylist.size()) {
        // The end was reached
        return -1;
    }
    return nextBatch;
}

线程将调用此方法并获取我们需要处理的范围:

int start = getNextBatch();
if(start == -1) {
    // The end was reached
}
int end = Math.min(start + batchSize, arraylist.size);

// Iterate over its own range
for(int i = start; i < end; i++) {
    Object obj = arraylist.get(i);
    // Do something with obj
}