我需要迭代一个大的ArrayList
(~50,000个条目),我需要使用多个线程来相当快地完成这个。
但我需要每个线程从一个唯一索引开始,这样就不会有两个线程迭代在列表的同一部分上。 batchSize
会100
,因此每个帖子都会从startIndex
循环到startIndex + 100
。
有没有办法实现这个目标?请注意,我只在这里执行读取操作,没有写入。列表中的每个条目只是一个String,它实际上是一个SQL查询,然后我通过JDBC对数据库执行。
答案 0 :(得分:5)
如果您只打算阅读List
,而不是改变它,则只需定义Runnable
即可将List
和startIndex
作为构造函数参数。只要没有线程同时修改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
}