Java中的非阻塞文件IO

时间:2010-08-30 15:00:38

标签: java nio named-pipes nonblocking

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

fopen(fPath, O_NONBLOCK)

因此,当读者出现时,它可能会从失败的地方恢复。

4 个答案:

答案 0 :(得分:8)

首先,我试着回答你的问题。接下来,我将尝试向您展示我创建的代码片段,它使用阻止IO来解决您的问题。

您的问题

  

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

您不需要非阻塞IO来解决您的问题。我认为它甚至无法帮助您解决问题。阻止IO也将运行良好(可能甚至比非阻塞IO更好,因为并发性低)。加号阻止IO更容易编程。你的读者可以/应该保持阻止。

  

我的读者是另一个应用程序   可能会下降。如果读者确实去了   我希望编写应用程序   写入命名管道。因此,当读者出现时,它可能会从失败的地方恢复。

将消息放入阻塞队列中。当读者从中读取命名管道时,仅写入 <由于阻塞IO而自动发生。使用阻塞队列时不需要非阻塞文件IO。当读者正在阅读时,数据是从阻塞队列异步传递的,这会将你的数据从你的作者发送给读者。

  

像fopen(fPath,   Java中的O_NONBLOCK)

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

CODE SNIPPET

创建了一个小片段,我相信它可以展示您的需求。

<强>组件:

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

<强> 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;
            }
        }
    }
}

答案 1 :(得分:2)

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

答案 2 :(得分:1)

如果Java中存在非阻塞文件I / O这样的事情,那么对未被读取的命名管道的写入将返回零并且不写入任何内容。因此,非阻塞不是解决方案的一部分。

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

答案 3 :(得分:-1)

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

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

......或......

 channel.write(..., myCompletionHandler);

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

这些都是不可克服的问题。如果你确定你可能会得到一些有用的东西。但是我想知道如果你刚刚使用TCP套接字或JMS队列,你是否会发现生活更轻松。