6

Been looking around for a little while now and I'm a bit confused on this issue. I want to be able to take an input stream and read it concurrently in segments. The segments don't interact with each other they are just values to be inserted or updated in a database from an uploaded file. Is it possible to read an input stream concurrently by setting a segment size and then just skipping forward before spinning off a new thread to handle the conversion and insert/update?

Essentially the file is a list of ID's (one ID per line), although it would be preferable if I could specify a separator. Some files can be huge so I would like to process and convert the data to be into segments so that after inserting/updating to the database the JVM memory can be freed up. Is this possible? And if so are there any libraries out there that do this already?

Cheers and thanks in advance,

Alexei Blue.

4

3 回答 3

7

一个好的方法可能是让一个读取器读取块,然后将每个块从线程池交给工作线程。鉴于这些将被插入到数据库中,与读取输入相比,插入将是迄今为止较慢的部分,因此单个线程应该足以读取。

下面是一个将每一行的处理从System.in工作线程移交给工作线程的示例。如果您在单个事务中执行大量插入,则数据库插入的性能要好得多,因此传递一组比如说 1000 行将比像示例中那样传递单行更好。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    public static class Worker implements Runnable {
        private final String line;

        public Worker(String line) {
            this.line = line;
        }

        @Override
        public void run() {
            // Process line here.
            System.out.println("Processing line: " + line);
        }
    }

    public static void main(String[] args) throws IOException {
        // Create worker thread pool.
        ExecutorService service = Executors.newFixedThreadPool(4);

        BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in));
        String line;

        // Read each line and hand it off to a worker thread for processing.
        while ((line = buffer.readLine()) != null) {
            service.execute(new Worker(line));
        }
    }
}
于 2013-04-23T01:17:38.867 回答
2

首先,要从不同的偏移量开始同时读取文件,您需要随机访问文件,这意味着从任何位置读取文件。Java 允许使用 java.in 中的 RandomAccessFile 或 java.nio 中的 SeekableByteChannel:

在 Java 文件中间写入字节的最佳方法

http://docs.oracle.com/javase/tutorial/essential/io/rafs.html

我认为出于速度的原因,您会更喜欢 java.nio。 Java NIO FileChannel 与 FileOutputstream 性能/实用性

现在您知道如何从任何位置读取,但您需要同时执行此操作。使用相同的文件访问对象是不可能的,因为它们在文件中占有位置。因此,您需要与线程一样多的文件访问对象。既然您是在阅读而不是在写作,那应该没问题。

现在您知道如何从许多不同的偏移量同时读取同一个文件。

但想想表现。尽管您只有一个磁盘驱动器和随机读取(许多线程访问同一个文件)的线程数,但性能比顺序读取(一个线程读取一个文件)慢得多。即使是突袭 0 或 1 - 也没关系。顺序读取总是快得多。因此,在您的情况下,我建议您在一个线程中读取文件,并为其他线程提供来自该读取线程的数据。

于 2013-04-23T06:09:41.493 回答
1

我认为您不能同时读取 InputStream 。这就是合约定义读取、重置和标记的原因——这个想法是流在内部跟踪已读取的内容和未读取的内容。

如果您正在读取文件,只需打开多个流。您可以使用skip()方法将标记向前移动以供其他线程避免重复行处理。 BufferedReader也可能有所帮助,因为它提供逐行阅读。

于 2013-04-23T01:19:26.177 回答