Java: System.out.println и System.err.println не работают

Мои вызовы System.out.println() и System.err.println() не выводятся на консоль в том порядке, в котором я их делаю.

 public static void main(String[] args) { for (int i = 0; i < 5; i++) { System.out.println("out"); System.err.println("err"); } } 

Это дает:

 out out out out out err err err err err 

Вместо того, чтобы чередовать и err . Почему это?

Они разные streamи и размываются в разное время.

Если вы положите

 System.out.flush(); System.err.flush(); 

внутри вашей петли, она будет работать как ожидалось.

Чтобы уточнить, выходные streamи кэшируются, поэтому вся запись идет в этот буфер памяти. После периода тишины они фактически выписаны.

Вы пишете два буфера, затем после периода бездействия они оба покраснели (один за другим).

Это вызвано особенностью JVM, и если вы не делаете взлома, например, предоставленный Marcus A., на самом деле это не так просто работать. В этом случае работает .flush() но причина этого намного сложнее в работе.

Что здесь происходит?

Когда вы программируете на Java, вы не говорите компьютеру, что делать, вы сообщаете JVM (виртуальная машина Java), что вы хотели бы сделать. И он будет делать это, но более эффективно. Ваш код не является точным подробным инструкциям, в этом случае вам нужен только компилятор, как на C и C ++, JVM берет ваш код в качестве списка спецификаций для того, что он должен оптимизировать, а затем делать. Это то, что происходит здесь . Java видит, что вы нажимаете строки в два разных streamа буферов. Самый эффективный способ сделать это – буферизировать все строки, которые вы хотите передать streamам, а затем вывести их. Это происходит по одному streamу в то время, по существу преобразуя ваш код, сделайте что-то вроде этого (будьте осторожны: псевдокод) :

 for(int i = 0; i < 5; i++) { out.add(); err.add(); } out.flush(); err.flush(); 

Поскольку это более эффективно, именно это будет делать JVM. Добавление .flush() в цикле будет сигнализировать JVM о необходимости промывки в каждом цикле, который не может быть улучшен с помощью вышеупомянутого метода. Но если вы объясните, как это будет работать, цикл JVM изменит порядок вашего кода, чтобы сделать печать последней, потому что это более эффективно.

 System.out.println("out"); System.out.flush(); System.err.println("err"); System.err.flush(); System.out.println("out"); System.out.flush(); System.err.println("err"); System.err.flush(); 

Этот код всегда будет реорганизован на что-то вроде этого:

 System.out.println("out");* System.err.println("err");* System.out.println("out");* System.err.println("err");* System.out.flush(); System.err.flush(); 

Поскольку буферизация многих буферов только для их очистки сразу после этого занимает гораздо больше времени, чем для буферизации всего буфера, а затем сбрасывает все это одновременно.

Как его решить

Это может привести к разработке кода и архитектуры; вы вроде бы не решаете этого. Чтобы обойти это, вы должны сделать его более эффективным для буферизации печати / флеша, печати буфера или флеша, чем буфер, а затем сброс. Это, скорее всего, соблазнит вас неудачным дизайном. Если для вас важно, как вывести его упорядоченно, я предлагаю вам попробовать другой подход. Для-looping с .flush() - это один из способов взломать его, но вы все еще взламываете функцию JVM, чтобы повторно организовать и оптимизировать свой код для вас.

* Я не могу проверить, что буфер, который вы добавили в первую очередь, всегда будет печатать первым, но, скорее всего, будет.

Если вы используете консоль Eclipse, на работе, похоже, работают два разных явления:
Один из них, как описано в @Gemtastic , является обработкой streamов JVM, а другой – тем, как Eclipse считывает эти streamи, как упоминает @DraganBozanovic . Так как я использую Eclipse, то из-за того,

В итоге я написал class-помощник под названием EclipseTools со следующим содержимым (и требуемым объявлением и импортом пакета). Это немного взломать, но исправляет обе проблемы:

 public class EclipseTools { private static List streams = null; private static OutputStream lastStream = null; private static class FixedStream extends OutputStream { private final OutputStream target; public FixedStream(OutputStream originalStream) { target = originalStream; streams.add(this); } @Override public void write(int b) throws IOException { if (lastStream!=this) swap(); target.write(b); } @Override public void write(byte[] b) throws IOException { if (lastStream!=this) swap(); target.write(b); } @Override public void write(byte[] b, int off, int len) throws IOException { if (lastStream!=this) swap(); target.write(b, off, len); } private void swap() throws IOException { if (lastStream!=null) { lastStream.flush(); try { Thread.sleep(200); } catch (InterruptedException e) {} } lastStream = this; } @Override public void close() throws IOException { target.close(); } @Override public void flush() throws IOException { target.flush(); } } /** * Inserts a 200ms delay into the System.err or System.out OutputStreams * every time the output switches from one to the other. This prevents * the Eclipse console from showing the output of the two streams out of * order. This function only needs to be called once. */ public static void fixConsole() { if (streams!=null) return; streams = new ArrayList(); System.setErr(new PrintStream(new FixedStream(System.err))); System.setOut(new PrintStream(new FixedStream(System.out))); } } 

Чтобы использовать, просто вызовите EclipseTools.fixConsole() один раз в начале вашего кода.

В принципе, это заменяет два streamа System.err и System.out с помощью настраиваемого набора streamов, которые просто пересылают свои данные в исходные streamи, но отслеживают, какой stream был написан для последнего. Если stream, который записывается для изменений, например System.err.something(...) за которым следует System.out.something(...) , он очищает вывод последнего streamа и ждет 200 мс, чтобы дать время консоли консоли Eclipse, чтобы завершить его печать.

Примечание: 200 мс – это просто приблизительное начальное значение. Если этот код уменьшает, но не устраняет проблему для вас, увеличьте задержку в Thread.sleep от 200 до уровня выше, пока он не будет работать. В качестве альтернативы, если эта задержка работает, но влияет на производительность вашего кода (если вы часто чередуете streamи), вы можете попытаться уменьшить его постепенно, пока не начнете получать ошибки.

Это ошибка в Eclipse . Кажется, что Eclipse использует отдельные streamи для чтения содержимого out и err streamов без какой-либо синхронизации.

Если вы скомпилируете class и выполните его в консоли (с classическим java

), порядок будет таким, как ожидалось.

Два оператора println обрабатываются двумя разными streamами. Результат снова зависит от того, в какой среде вы запускаете код. Например, я выполнил следующий код в IntelliJ и в командной строке по 5 раз.

 public class Test { public static void main(String[] args) { for (int i = 0; i < 10; i++) { System.out.print("OUT "); System.err.print("ERR "); } } } 

Это приводит к следующему результату:
Командная строка

 OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR 

IntelliJ:

 ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR 

Я думаю, что разные среды обрабатывают буферы по-разному.
Один из способов увидеть, что эти streamи обрабатываются различными streamами, заключается в том, чтобы добавить оператор sleep в цикл. Вы можете попробовать изменить значение, которое вы задали для сна, и посмотреть, что они являются infact, обрабатываемыми разными streamами.

 public class Test { public static void main(String[] args) { for (int i = 0; i < 10; i++) { System.out.print("OUT "); System.err.print("ERR "); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } 

Результат в этом случае оказался

 OUT ERR ERR OUT ERR OUT OUT ERR OUT ERR ERR OUT OUT ERR ERR OUT OUT ERR OUT ERR OUT ERR ERR OUT ERR OUT OUT ERR OUT ERR ERR OUT OUT ERR ERR OUT OUT ERR OUT ERR ERR OUT ERR OUT OUT ERR ERR OUT OUT ERR ERR OUT OUT ERR OUT ERR ERR OUT ERR OUT ERR OUT OUT ERR ERR OUT OUT ERR ERR OUT ERR OUT OUT ERR ERR OUT ERR OUT OUT ERR OUT ERR OUT ERR ERR OUT OUT ERR ERR OUT OUT ERR ERR OUT ERR OUT OUT ERR OUT ERR 

Один из способов заставить его распечатать его в том же порядке, будет использовать .flush() , который работал для меня. Но, по-видимому, не все получают правильные результаты.

Два streamа, обработанные двумя двумя разными streamами, вероятно, являются причиной того, что мы иногда видим сообщение ERROR напечатанное некоторыми библиотеками, которые мы используем, печатая перед некоторыми заявлениями о печати, которые мы должны были видеть в соответствии с порядком выполнения.

Я использовал stream для вывода вывода System.out и System.err последовательно:

  for(int i = 0; i< 5; i++){ try { Thread.sleep(100); System.out.print("OUT"); Thread.sleep(100); System.err.print("ERR"); }catch (InterruptedException ex){ System.out.println(ex.getMessage()); } } 
  • Хорошее введение в .NET Reactive Framework
  • получить уникальный идентификатор машины
  • С обновлением версии Java 7 45 свойства системы больше не установлены с тега JNLP «Свойство»,
  • Угловой провайдер для NameService
  • Отображать специальные символы, используя System.out.println
  • Почему в Android не работает «System.out.println»?
  • Interesting Posts

    Исключение брошено внутри блока catch – будет ли оно поймано снова?

    Инициировать объект с определенным временем выполнения

    Значение ошибки , если (квадратные скобки)

    Oracle Искать все таблицы для всех столбцов для строки

    Как я могу сделать работу моего F-ключа в F-ключах?

    В чем преимущество ViewHolder?

    Android: При каких обстоятельствах будет вызываться диалог, вызывающий onPause ()?

    #pragma comment (lib, “xxx.lib”) эквивалент Linux?

    Установить представление папки по умолчанию в Windows

    Блокнот ++: как добавить возврат каретки (нажатие клавиши ввода) после определенного символа

    Возможно ли создать индивидуальные настройки питания для каждого жесткого диска?

    Взаимоотношения Groovy в отношении пространства / времени

    Почему C ++ не может быть проанализирован парсером LR (1)?

    Явное построение типа объекта ‘###’ в запросе недопустимо.

    React Native error: «Невозможно определить версию java из« 9.0.1 ».»

    Давайте будем гением компьютера.