2

从 PrintStream文档

可选地,可以创建一个 PrintStream 以便自动刷新;这意味着在写入字节数组、调用 println 方法之一或写入换行符或字节 ('\n')会自动调用 flush 方法

然后给出代码

System.out.print("hi");   // gives console output: hi
System.out.print(7);      // gives console output: 7

// prevents flushing when stream wiil be closed at app shutdown
for (;;) {
}

为什么我会看到控制台的输出?不应将任何内容写入控制台(来自 System.out 的 PrintStream 实例),因为到目前为止不应刷新任何内容!

没有回答这个问题。

我想,答案在源代码中(私有实用程序方法 BufferedWriter.flushBuffer()),但我不明白对代码的注释:“将输出缓冲区刷新到底层字符流,而不刷新流本身”:如果 PrintStream(与控制台输出相关联),即“流本身”未刷新,则不应刷新到控制台的输出!...

PrintStream.print(String) 的来源:

private void write(String s) {
        try {
            synchronized (this) {
                ensureOpen();
                textOut.write(s);
                textOut.flushBuffer();
                charOut.flushBuffer();
                if (autoFlush && (s.indexOf('\n') >= 0))
                    out.flush();
            }
        }
        catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        }
        catch (IOException x) {
            trouble = true;
        }
    }

BufferedWriter.flushBuffer() 的来源:

/**
     * Flushes the output buffer to the underlying character stream, without
     * flushing the stream itself.  This method is non-private only so that it
     * may be invoked by PrintStream.
     */
    void flushBuffer() throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (nextChar == 0)
                return;
            out.write(cb, 0, nextChar);
            nextChar = 0;
        }
    }

此处还提供了更多详细信息。它非常复杂,但似乎在某个阶段 BufferedWriter 被赋予了 PrintStream 构造函数。

4

2 回答 2

2

我使用调试器一步一步地进行,这就是我发现的: 在第 527 行之后显示在控制台中,所以它在第 528 行之前完成了检查。在此处输入图像描述 String s\n

charOut.flushBuffer()内部,有以下方法被调用:在此处输入图像描述

其中,检查\n缺失。

流程如下:

  1. System.out#print(String s)来电PrintStream#print(String s)
  2. PrintStream#print(String s)来电PrintStream#write(String s)
  3. PrintStream#write(String s)来电OutputSteamWriter#flushBuffer()
  4. OutputStreamWriter#flushBuffer()来电StreamEncoder#flushBuffer()
  5. StreamEncoder#flushBuffer()来电StreamEncoder#implFlushBuffer()
  6. StreamEncoder#implFlushBuffer()来电StreamEncoder#writeBytes()
  7. StreamEncoder#writeBytes()PrintStream#write(byte buf[], int off, int len)刷新缓冲区的调用if(autoFlush)

最重要的片段在上面。在这个BufferedWriter流程中似乎没有被调用。

于 2017-06-07T09:06:41.463 回答
0

https://bugs.openjdk.java.net/browse/JDK-8025883描述了这个错误。

这让我陷入了一个读取和解析二进制文件的程序,做了很多System.out.printf()调用,这比它应该花费的时间更长。

我最终做的是编写一个辅助类,它违反了 Streams 的合同,不遵守每个刷新请求:

class ForceBufferedOutputStream extends OutputStream {

    OutputStream out;
    byte[] buffer;
    int buflen;
    boolean haveNewline;
    private static final int bufsize=16384;

    public ForceBufferedOutputStream(OutputStream out) {
        this.out=out;
        this.buffer=new byte[bufsize];
        this.buflen=0;
        this.haveNewline=false;
    }
    @Override
    public void flush() throws IOException {
        if (this.haveNewline || this.buflen==bufsize) {
            out.write(buffer, 0, buflen);
            out.flush();
            this.buflen=0;
            this.haveNewline=false;
        }
    }
    @Override
    public void close() throws IOException {
        out.close();
    }

    @Override
    public void write(int b) throws IOException {
        buffer[buflen++]=(byte)b;
        if (b=='\n')
            this.haveNewline=true;
        if (buflen==bufsize)
            this.flush();
    }
}

然后使用 anew PrintStream(new ForceBufferedOutputStream(System.out))而不是System.out.

我认为这是一个可怕的软件 - 如上所述,它违反了flush()需要确保所有内容都已写入的合同,并且它可以优化数组write调用。但在我的例子中,运行时间从 17 分钟缩短到 3:45,所以如果你需要一个复制/粘贴来加速一个快速而肮脏的程序类型,我希望它有所帮助。

于 2018-12-20T18:24:23.687 回答