Java:异步并发写入磁盘

时间:2014-11-13 07:53:46

标签: java multithreading java.util.concurrent

我的主线程中有一个函数会将一些数据写入磁盘。我不希望我的主线程卡住(磁盘I / O的高延迟)并且创建一个新线程只是为了写入是一个矫枉过正。我决定使用 ExecutorService

ExecutorService executorService = Executors.newFixedThreadPool(3);

   Future future = executorService.submit(new Callable<Boolean>() {
    public Boolean call() throws Exception {
      logger.log(Level.INFO, "Writing data to disk");
      return writeToDisk();
    }
  });

writeToDisk 是将写入磁盘的函数

这是一个很好的方式吗?有人可以提出更好的方法吗?

更新:数据大小将大于100 MB。磁盘带宽为40 MBps,因此写入操作可能需要几秒钟。我不希望调用函数卡住,因为它必须做其他工作,所以,我正在寻找一种方法来调度磁盘I / O异步执行调用线程。

我需要委派任务而忘记它!

3 个答案:

答案 0 :(得分:7)

无论如何我的代码看起来都很好我从新的非阻塞IO中使用了AsynchronousFileChannel。该实施使用MappedByteBufferFileChannel。它可能会给你@Chris所说的表现。以下是一个简单的例子:

public static void main(String[] args) {
    String filePath = "D:\\tmp\\async_file_write.txt";
    Path file = Paths.get(filePath);
    try(AsynchronousFileChannel asyncFile = AsynchronousFileChannel.open(file,
                        StandardOpenOption.WRITE,
                        StandardOpenOption.CREATE)) {

        asyncFile.write(ByteBuffer.wrap("Some text to be written".getBytes()), 0);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

答案 1 :(得分:2)

我知道有两种方法,启动一个线程(或者使用你拥有的线程池)或内存映射文件并让操作系统管理它。两者都是很好的方法,内存映射文件可以比使用Java编写器/流快10倍,并且它不需要单独的线程,所以当性能是关键时我经常偏向于它。

无论哪种方式,作为优化磁盘写入的一些技巧,尝试尽可能预先分配文件。调整文件大小非常昂贵。旋转磁盘不喜欢寻找,而且SSD不喜欢改变以前写过的数据。

我写了一些基准来帮助我探索这个区域一段时间,随意自己运行benchmarks。其中有一个内存映射文件的例子。

答案 2 :(得分:2)

我同意Õzbek使用非阻塞方法。然而,正如Dean Hiller指出的那样,我们无法使用资源尝试关闭AsynchronousFileChannel,因为它可能会在写入完成之前关闭通道。

因此,我会在CompletionHandler上添加asyncFile.write(…,new CompletionHandler< >(){…}来跟踪完成情况,并在写入操作结束后关闭基础AsynchronousFileChannel。为了简化使用,我将CompletionHandler转换为CompletableFuture,我们可以在写完成后轻松链接并继续执行任何操作。最后的辅助方法是CompletableFuture<Integer> write(ByteBuffer bytes),它在完成相应的写操作后返回最终文件索引的CompletableFuture。 我将所有这些逻辑放在辅助类AsyncFiles中,可以像这样使用:

Path path = Paths.get("output.txt")
AsyncFiles
    .write(path, bytes)
    .join(); // block if you want to wait for write completion