将List Iterator传递给Java中的多个线程

时间:2016-02-05 11:04:37

标签: java multithreading iterator listiterator

我有一个包含大约200K元素的列表。

我是否能够将此列表的迭代器传递给多个线程并让它们遍历整个批次,而没有任何一个访问相同的元素?

这就是我现在所想的。

主:

public static void main(String[] args)
{
    // Imagine this list has the 200,000 elements.
    ArrayList<Integer> list = new ArrayList<Integer>();

    // Get the iterator for the list.
    Iterator<Integer> i = list.iterator();

    // Create MyThread, passing in the iterator for the list.
    MyThread threadOne = new MyThread(i);
    MyThread threadTwo = new MyThread(i);
    MyThread threadThree = new MyThread(i);

    // Start the threads.
    threadOne.start();
    threadTwo.start();
    threadThree.start();
}

MyThread的:

public class MyThread extends Thread
{

    Iterator<Integer> i;

    public MyThread(Iterator<Integer> i)
    {
        this.i = i;
    }

    public void run()
    {
        while (this.i.hasNext()) {
            Integer num = this.i.next();
            // Do something with num here.
        }
    }
}

我希望得到的结果是每个线程每个处理大约66,000个元素,而不会过多地锁定迭代器,并且没有任何线程访问相同的元素。

这听起来有用吗?

5 个答案:

答案 0 :(得分:4)

真的需要手动操作线程和迭代器吗?您可以使用Java 8 Stream并让parallel()完成工作。

默认情况下,它会使用少一个线程,因为你有处理器。

示例:

list.stream()
    .parallel()
    .forEach(this::doSomething)
;

//For example, display the current integer and the current thread number.
public void doSomething(Integer i) {
  System.out.println(String.format("%d, %d", i, Thread.currentThread().getId()));
}

结果:

49748, 13
49749, 13
49750, 13
192710, 14
105734, 17
105735, 17
105736, 17
[...]

编辑:如果您使用的是maven,则需要在pom.xml中添加此配置才能使用Java 8:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.3</version>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
      </configuration>
    </plugin>
  </plugins>
</build>

答案 1 :(得分:2)

使用单个迭代器无法以线程安全的方式执行此操作。我建议使用子列表:

List sub1 = list.subList(0, 100);
List sub2 = list.subList(100, 200);

ArrayList#subList()方法将只包装给定列表而不复制元素。然后,您可以在不同的线程中迭代每个子列表。

答案 2 :(得分:1)

由于实现next()接口的类的Iterator方法执行数据操作,因此next()方法的并发使用需要同步。可以使用迭代器对象上的synchronized块完成同步,如下所示:

synchronized(i)
{
    i.next();
}

尽管如此,如果您只需要并行处理列表,我建议使用Stream API,如上面的答案。

答案 3 :(得分:0)

您可以使用线程池类中的ExecutorService来防止线程发生长线程序或饥饿。对于我来说,这比使用synchronized,lock或Re-entrant-locks更好。您也可以尝试使用Fork / join,但我之前没有使用它。 这是一个示例代码,但我希望你能得到这个想法

public static void main(String[] args){
   ExecutorService executor = Executors.newFixedThreadPool(200000);
   List<Future<Integer>> futureList = new ArrayList<>();
   //iteration code goes here
  executor.shutdown();
}

Public class MyThread implements Callable<ArrayList<Integer>>{

@Override
        public Iterator<Integer> call() throws Exception {
            //code goes here!
        }  

}

答案 4 :(得分:0)

如果你使用并行流,你将跨多个线程执行代码,元素在线程之间均匀分布:

list.parallelStream().forEach(this::processInteger);

这种方法使编码变得非常简单;所有繁重的工作都由JRE完成。

此外,关于您的代码,扩展Thread是不好的方式。相反,实现Runnable并将实例传递给Thread的构造函数 - 请参阅实时