Вывод System.out.print на консоль виден сразу. То есть PrintStream сбрасывает после каждой печати, а не только println?
Из документации PrintStream:
При желании можно создать PrintStream для автоматической очистки; это означает, что метод flush вызывается автоматически после записи байтового массива, запускается один из методов println или пишется символ новой строки или байт ('\n').
Тогда дан код
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 (;;) {
}
Почему тогда я вижу вывод на мою консоль? Ничто не должно быть записано в консоль (экземпляр PrintStream из System.out), потому что пока ничего не должно быть сброшено!
Это не ответило на это.
Я предполагаю, что ответ находится в исходном коде (метод частной утилиты 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.
1 ответ
Я пошел шаг за шагом, используя отладчик, и вот что я нашел: String s
отображается в консоли после 527-й строки, поэтому перед строкой 528, в которой проверяется наличие \n
готово.
В charOut.flushBuffer()
глубоко внутри существует следующий метод:
В котором, проверка о \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)
который сбрасывает буфорif(autoFlush)
,
Самые важные фрагменты выше. BufferedWriter
кажется, не вызывать в этом потоке.
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();
}
}
затем с помощью new PrintStream(new ForceBufferedOutputStream(System.out))
вместо System.out
,
Я считаю, что это ужасная часть программного обеспечения - как сказано, это нарушает контракт, который flush()
необходимо убедиться, что все написано, и это может оптимизировать массив write
звонки. Но в моем случае время выполнения было сокращено с 17 минут до 3:45, поэтому, если вам нужна функция копирования / вставки, которая ускоряет быстрые и грязные программы, я надеюсь, что это поможет.