【问题标题】:Using a threadpool to add in to a list使用线程池添加到列表中
【发布时间】:2017-09-26 13:34:01
【问题描述】:

我正在尝试读取文件并将每一行添加到列表中。

Simple drawing explaining the goal

主类-

public class SimpleTreadPoolMain {

  public static void main(String[] args) {
    ReadFile reader = new ReadFile();
    File file = new File("C:\\myFile.csv");
    try {
        reader.readFile(file);
    } catch (IOException e) {
        e.printStackTrace();
    }
  }
}

阅读器类 -

public class ReadFile {

ExecutorService executor = Executors.newFixedThreadPool(5);//creating a pool of 5 threads

List<String> list = new ArrayList<>();

void readFile(File file) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(file))) {
        String line;
        while ((line = br.readLine()) != "") {
            Runnable saver = new SaveToList(line,list);  
            executor.execute(saver);//calling execute method of ExecutorService 
        }
    }

    executor.shutdown();  
    while (!executor.isTerminated()) {   }  

}

}

保护类 -

public class SaveToList<E> implements Runnable{

List<E> myList;

E line;

public SaveToList(E line, List<E> list) {
    this.line = line;
    this.myList = list;
}

public void run() {
    //modify the line
    myList.add(line);

}
}

我尝试将许多保护程序线程添加到同一个列表中,而不是一个保护程序一个一个地添加到列表中。我想使用线程,因为我需要在添加到列表之前修改数据。所以我认为修改数据需要一些时间。所以并行这部分会减少时间消耗,对吧?

但这不起作用。我无法返回包含文件中所有值的全局列表。我只想从文件中获得一个全局值列表。所以代码肯定应该改变。如果有人可以指导我,将不胜感激。

即使在单个线程中逐一添加也可以,但使用线程池会更快,对吧?

【问题讨论】:

  • 除了一个一个一个之外,您认为您可以如何添加到列表中?一件事必须进去;然后是下一个;然后是下一个。
  • 你能详细说明什么不起作用吗? 但这不起作用。有点笼统
  • 那你是说使用少线程不会影响插入部分的时间消耗?
  • 他说的完全不是这个意思
  • 请注意,while ((line = br.readLine()) != "") { 不会按照您的想法行事。见How do I compare strings in Java?

标签: java multithreading threadpool threadpoolexecutor


【解决方案1】:

实际上,您应该尝试在您的应用程序中使用多线程是否值得,只需比较读取整个文件而不对已完成的行进行任何处理所需的时间,并将其与串行处理整个文件所需的时间进行比较文件。

如果你的过程不是太复杂,我猜是不值得使用多线程。

如果您发现所花费的时间要多得多,那么您可以考虑使用一个或多个线程来进行计算。

如果是这样,您可以使用Futures 处理批量输入字符串,或者您可以使用线程安全队列将字符串发送到另一个进程。

private static final int BATCH_SIZE = 1000;

public static void main(String[] args) throws IOException {

    BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("big_file.csv"), "utf-8"));


    ExecutorService pool = Executors.newFixedThreadPool(8);
    String line;
    List<String> batch = new ArrayList<>(BATCH_SIZE);
    List<Future> results = new LinkedList<>();
    while((line=reader.readLine())!=null){
        batch.add(line);
        if(batch.size()>=BATCH_SIZE){
            Future<Object> f = noWaitExec(batch, pool);
            results.add(f);
            batch = new ArrayList<>(BATCH_SIZE);
        }
    }
    Future<List> f = noWaitExec(batch,pool);
    results.add(f);

    for (Future future : results) {
        try {
            Object object = future.get();
            // Use your results here 
        } catch (Exception e) {
            // Manage this....
        }
    }


}
private static Future<List> noWaitExec(final List<String> batch, ExecutorService pool) {
    return pool.submit(new Callable<List>() {
        public List call() throws Exception {
            List result = new ArrayList<>(batch.size());
            for (String string : batch) {
                result.add(process(string));
            }
            return result;
        }

    });
}

private static Object process(String string) {
    // Your process .... 
    return null;
};

还有很多其他可能的解决方案(Observables, ParallelStreams, Pipes, CompletableFutures ...你的名字),但我认为大部分时间都花在读取文件上,只是使用 BufferedInputStream 来读取具有足够大缓冲区的文件可以比并行计算节省更多时间。

【讨论】:

    【解决方案2】:

    在这里使用多个线程不会加快任何速度。

    你是:

    • 从文件中串行读取一行。
    • 创建一个可运行对象并将其提交到线程池中
    • runnable 然后将内容添加到列表中

    鉴于您使用的是ArrayList,您需要同步对它的访问,因为您正在从多个线程中对其进行变异。因此,您正在将内容连续添加到列表中。

    但是即使没有同步,IO 所花费的时间也会远远超过将字符串添加到列表中所花费的时间。并且添加多线程只会让它更慢,因为它正在做构建可运行的工作,将其提交到线程池,调度它等等。

    省略整个中间步骤会更简单:

    • 从文件中串行读取一行。
    • 按顺序将列表添加到列表中。

    所以:

    try (BufferedReader br = new BufferedReader(new FileReader(file))) {
        String line;
        while (!(line = br.readLine()).isEmpty()) {
            list.add(line);
        }
    }
    

    【讨论】:

    • +1 为答案。你有什么想法让它更快,因为我正在阅读非常大的 CSV 文件。在添加到列表之前,我正在修改值。如果我使用 HashMap 或 Set,使用线程是否有意义?
    • "如果我使用 HashMap 或 Set" 再次,你需要同步它们。
    • 这看起来不像回答了这个问题,但是我真的不明白这个问题问的是什么。你说的事情在技术上是正确的。虽然你可以通过使用多通道和随机访问文件来加速 IO,但我相信。
    • @Hasith,请编辑问题以提及您正在修改行,而不仅仅是将它们添加到列表中。并行化修改可能比并行化添加更有意义。
    • 我猜几乎没有办法修改单个内存中的String 与从文件中读取相同的String 所花费的时间相当。这将需要一组非常特殊的操作,或者说,在数据库或其他东西上完成的几个补充阻塞 I/O 操作。
    猜你喜欢
    • 2011-02-06
    • 2016-08-10
    • 2011-07-18
    • 2012-05-24
    • 2015-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多