【问题标题】:Java - Process bytes as they are being read from a fileJava - 在从文件中读取字节时处理字节
【发布时间】:2012-08-02 20:25:33
【问题描述】:

有没有办法让 java 中的一个线程对某些 FileInputStream 或类似内容进行读取调用,并让第二个线程同时处理正在加载的字节? 我已经尝试了很多东西 - 我目前的尝试有一个线程运行这个:

FileChannel inStream;
try {
   inStream = (new FileInputStream(inFile)).getChannel();
} catch (FileNotFoundException e) {
    e.printStackTrace();
}
int result;
try {
     result = inStream.read(inBuffer);
} ...

第二个线程想要在加载字节时访问它们。显然,第一个线程中的读取调用会阻塞,直到缓冲区已满,但我希望能够在该点之前访问加载到缓冲区中的字节。目前,我尝试的所有东西都有缓冲区,并且它的支持数组在读取完成之前保持不变 - 这不仅破坏了这个线程的要点,而且还表明数据正在某个地方加载到某个中间缓冲区,然后稍后复制到我的缓冲区中,这似乎愚蠢。

一种选择是对数组进行一系列较小的读取,并在后续读取时使用偏移量,但这会增加额外的开销。

有什么想法吗?

【问题讨论】:

  • 我考虑的另一个选项是使用 PipedInput/Output 流。这似乎可能会起作用,但这样做会增加开销 - 为什么我不能让我的 Filechannel 或 FileInputStream “流”到我的 ByteBuffer 或某个字节数组中?
  • “显然第一个线程中的读取调用会阻塞,直到缓冲区已满”它不应该。对read() 的调用应该阻塞,直到数据可用。 (操作系统的 IO 子系统负责将数据传送到特定于流的缓冲区。)另外,为什么在这个用例中使用 NIO?您似乎没有在任何地方使用它的功能。
  • 它不会“在缓冲区满之前阻塞”。它从流中读取至少一个字节,阻塞直到至少一个字节可用。无论如何,因为它是一个文件,所以基本上没有真正的阻塞。你不需要两个线程来解决这个问题。第二个必须阻止第一个,第一个必须阻止输入。没有任何收获。

标签: java multithreading asynchronous io filechannel


【解决方案1】:

当您顺序读取数据时,操作系统会在您需要数据之前提前读取数据。由于系统已经为您执行此操作,您可能无法获得预期的好处。

为什么我不能让我的 Filechannel 或 FileInputStream “流”到我的 ByteBuffer 或某个字节数组中?

这就是它已经在做的事情了。

如果您想要更无缝地加载数据,您可以使用内存映射文件,因为它会立即“出现”在程序的内存中,并在您使用时在后台加载。

【讨论】:

    【解决方案2】:

    我通常对这样的需求做的是使用多个缓冲区类实例,最好调整大小以允许有效加载 - 例如集群大小的倍数。一旦第一个缓冲区加载完毕,就将其排入队列(即将其指针/实例推送到生产者-消费者队列中)到将处理它的线程并立即创建(或解池)另一个缓冲区实例和开始加载那个。为了控制整体数据流,您可以在启动时创建适当数量的缓冲区对象并将它们存储在“池队列”(另一个生产者-消费者队列)中,然后您可以从池中循环充满数据的对象,以文件读取线程,然后到缓冲区处理线程,然后返回池。

    这使文件->处理队列“充满”缓冲区对象充满数据,不需要批量复制,没有不可避免的延迟,没有单字节的低效线程间通信,没有混乱的缓冲区索引锁定,文件读取线程和数据处理线程不可能对同一个缓冲区对象进行操作。

    如果您想要/需要使用线程池来执行处理,您可以轻松地执行此操作,但如果您需要此子系统的任何结果输出与它是从文件中读取的。

    缓冲区对象还可能包含结果数据成员、异常/错误消息字段以及您可能想要的任何内容。在重新汇集之前,文件和/或结果数据可以很容易地从数据处理转发到其他线程(例如,记录器或进度的 GUI 显示)。由于这只是指针/实例队列,因此大量数据将在您的系统中快速有效地流动。

    【讨论】:

    • 感谢您的建议 - 到目前为止,这似乎运作良好。似乎根本无法在不使用内存映射的情况下在调用运行时访问正在进行的读取调用中的字节。
    【解决方案3】:

    我建议使用SynchronousQueue。读取器将从队列中检索数据,写入器将从您的文件中“发布”数据。

    【讨论】:

      【解决方案4】:

      使用 PipedInput/OutputStream 创建一个外观熟悉的带有缓冲区的管道。?

      如有必要,还可以使用 FileInputStream 逐字节读取它。 fis.read()函数不会阻塞,如果没有数据它会返回-1,你可以随时检查available();

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多