从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的源代码。打印(字符串):
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构造函数。
其中,缺少关于\n
的检查。
流程如下:
System.out#print(String s)
调用PrintStream#print(String s)
。PrintStream#print(String s)
调用PrintStream#write(String s)
。PrintStream#write(String s)
调用OutputSteamWriter#flushBuffer()
。OutputStreamWriter#flushBuffer()
调用StreamEncoder#flushBuffer()
。StreamEncoder#flushBuffer()
调用StreamEncoder#implFlushBuffer()
。StreamEncoder#implFlushBuffer()
调用StreamEncoder#WriteBytes()
。StreamEncoder#WriteBytes()
调用PrintStream#write(byte buf[], int off, int len)
刷新bufforif(autoFlush)
。最重要的片段在上面。BufferedWriter
似乎没有在此流程中调用。
https://bugs.openjdk.java.net/browse/JDK-8025883描述了这个错误。
这让我在一个读取和解析二进制文件的程序中受益匪浅,在系统中执行了大量的。出来printf()
调用,这需要更长的时间。
最后我写了一个helper类,它违反了Streams的约定,没有满足每个flush请求:
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();
}
}
然后使用new PrintStream(new ForceBufferedOutputStream(System.out))
代替System.out
。
我认为这是一个可怕的软件——正如前面所说,它违反了合同,即flush()
需要确保所有内容都已写入,并且它可以优化数组写入
调用。但在我的例子中,运行时间从17分钟缩短到了3:45,所以如果你需要一个能加速快速而肮脏的程序的复制/粘贴,我希望它能有所帮助。