【问题标题】:Non-Blocking File IO in JavaJava中的非阻塞文件IO
【发布时间】:2010-08-30 15:00:38
【问题描述】:

我想写入一个命名管道(已经创建)而不阻塞阅读器。我的阅读器是另一个可能会失败的应用程序。如果阅读器确实出现故障,我希望编写器应用程序继续写入该命名管道。 Java中的类似这样的东西

fopen(fPath, O_NONBLOCK)

这样当阅读器出现时,它可以从失败的地方恢复。

【问题讨论】:

  • 我不确定你是否可以用管道做到这一点。我相信如果阅读器或作者关闭,管道就会吐司。这是 PipedOutputStream 的 Javadoc 中的一句话:“如果从连接的管道输入流中读取数据字节的线程不再活动,则称管道已损坏”。我不认为损坏的管道可以修复。
  • @Jim Tough 他在谈论命名管道(linuxjournal.com/article/2156)。不是来自 Java SDK 的 PipedOutputStream。
  • 不是您可以接受的答案?
  • 您真的是指 UNIX 命名管道吗?就像用 mkfifo 创建的一样?

标签: java nio named-pipes nonblocking


【解决方案1】:

首先,我尝试回答您的问题。接下来我将尝试向您展示我创建的代码 sn-p 使用阻塞 IO 解决您的问题。

您的问题

我想写一个命名管道 (已经创建)没有阻塞 读者

您不需要非阻塞 IO 来解决您的问题。我认为它甚至不能帮助你解决你的问题。阻塞 IO 也会运行良好(由于并发性低,可能比非阻塞 IO 更好)。一个优点是阻塞 IO 更容易编程。您的读者可以/应该保持屏蔽状态。

我的阅读器是另一个应用程序 可能会下降。如果读者真的去了 下来,我希望作家应用程序 neep 写入命名管道。这样当阅读器出现时,它可以从失败的地方恢复。

只需将消息放入阻塞队列即可。接下来在读取器读取命名管道时写入(由于阻塞 IO 而自动发生)。使用阻塞队列时不需要非阻塞文件 IO。当读取器正在读取时,数据从阻塞队列异步传递,这会将您的数据从写入器发送到读取器。

类似于 fopen(fPath, Java 中的 O_NONBLOCK)

您不需要阅读器上的非阻塞 IO,即使您使用它也是如此。只需使用阻塞 IO。

代码片段

A 创建了一个小 sn-p,我相信它可以证明您的需求。

组件:

  • Writer.java:例如从控制台读取行。当您启动程序时输入文本,然后输入将其发送到您的命名管道。如有必要,作者将继续写作。
  • Reader.java:读取从命名管道 (Writer.java) 写入的行。
  • 命名管道:我假设您在同一目录中创建了一个名为“管道”的管道。

Writer.java

import java.io.BufferedWriter;
import java.io.Console;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Writer {
    private final BlockingDeque<StringBuffer> queue;
    private final String filename;

    public static void main(String[] args) throws Exception {
        final Console console = System.console();
        final Writer writer = new Writer("pipe");

        writer.init();

        while(true) {
            String readLine = console.readLine();
            writer.write(new StringBuffer(readLine));
        }
    }

    public Writer(final String filename){
        this.queue = new LinkedBlockingDeque<StringBuffer>();
        this.filename = filename;
    }

    public void write(StringBuffer buf) {
        queue.add(buf);
    }

    public void init() {
        ExecutorService single = Executors.newSingleThreadExecutor();

        Runnable runnable = new Runnable() {
            public void run() {
                while(true) {
                    PrintWriter w = null;
                    try {
                        String toString = queue.take().toString();
                        w = new PrintWriter(new BufferedWriter(new FileWriter(filename)), true);
                        w.println(toString);
                    } catch (Exception ex) {
                        Logger.getLogger(Writer.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        };

        single.submit(runnable);
    }
}

Reader.java

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Reader {
    private final BufferedReader br;

    public Reader(final String filename) throws FileNotFoundException {
        br = new BufferedReader(new FileReader(filename));
    }

    public String readLine() throws IOException {
        return br.readLine();
    }

    public void close() {
        try {
            br.close();
        } catch (IOException ex) {
            Logger.getLogger(Reader.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void main(String[] args) throws FileNotFoundException {
        Reader reader = new Reader("pipe");
        while(true) {
            try {
                String readLine = reader.readLine();
                System.out.println("readLine = " + readLine);
            } catch (IOException ex) {
                reader.close();
                break;
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    如果您希望管道保持活动状态并将消息排队,您可能需要一个消息系统而不是原始管道。在 Java 中,标准 API 称为“Java 消息传递系统”(JMS),并且有许多标准实现——我见过的最常见的是 Apache ActiveMQ。如果你想要一个跨平台的、类似套接字的接口来进行缓冲和恢复,我可能会建议 0MQ,虽然它不是“纯 Java”,但它具有多种语言的绑定和出色的性能。

    【讨论】:

      【解决方案3】:

      如果在 Java 中存在诸如非阻塞文件 I/O 之类的东西,但实际上并没有,那么对未被读取的命名管道的写入将返回零并且不会写入任何内容。所以非阻塞不是解决方案的一部分。

      还有一个问题是命名管道的缓冲区大小有限。无论是否有读取过程,它们都不是无限队列。我同意调查 JMS 的建议。

      【讨论】:

      • 有非阻塞文件IO。搜索蔚来(en.wikipedia.org/wiki/New_I/O)
      • NIO 和 Java 的权威参考通常不是 Wikipedia,而是 Javadoc,它没有这么说。维基百科也没有这件事。 NIO 代表“新 I/O”,尽管有维基百科。
      • 我知道这是一个旧的问答,但为了正确起见,值得指出的是 Java 7 有 AsynchronousFileChannel
      • @nilskp 为了正确起见,值得指出的是异步 I/O 与非阻塞 I/O 不同,异步写入管道被读取的 t 永远不会终止,这使得它也不是解决方案的一部分。
      【解决方案4】:

      您应该能够在 UNIX FIFO 上使用 NIO 的 asynch write,就像对任何其他文件一样:

       AsynchronousFileChannel channel = AsynchronousFileChannel.open(...);
       Future<Integer> writeFuture = channel.write(...);
      

      ...或...

       channel.write(..., myCompletionHandler);
      

      但是,当 FIFO 不接受写入时,我不清楚您希望发生什么。你想让它缓冲吗?如果是这样,您需要在 Java 程序中提供它。你想让它超时吗? Java 文件写入没有简单的超时选项。

      这些不是无法克服的问题。如果你下定决心,你可能会得到一些工作。但是我想知道如果您只使用 TCP 套接字或 JMS 队列,您是否会发现生活不会变得更轻松。

      【讨论】:

      • 如果您检查 javadoc 的 AsynchronousFileChannel ,它会声明:“An AsynchronousFileChannel is associated with a thread pool ...”。所以,这个类并没有真正提供非阻塞 API,只是异步......
      • 在 UNIX FIFO 上使用AsynchronousFileChannel 的主要问题是write() 操作(以及read())需要一个写入位置,然后在写入之前在文件描述符上使用seek() - 这对 FIFO 来说是一个很大的禁忌。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-17
      • 2013-05-24
      • 1970-01-01
      • 2014-09-25
      • 2017-09-18
      • 1970-01-01
      相关资源
      最近更新 更多