java System.out.println 和 System.err.println 乱序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1883321/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
System.out.println and System.err.println out of order
提问by Nick Heiner
My System.out.println()and System.err.println()calls aren't being printed to the console in the order I make them.
我的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");
}
}
This produces:
这产生:
out
out
out
out
out
err
err
err
err
err
Instead of alternating outand err. Why is this?
而不是交替out和err。为什么是这样?
采纳答案by Bill K
They are different streams and are flushed at different times.
它们是不同的流并且在不同的时间被冲洗。
If you put
如果你把
System.out.flush();
System.err.flush();
inside your loop, it will work as expected.
在您的循环中,它将按预期工作。
To clarify, output streams are cached so all the write goes into this memory buffer. After a period of quiet, they are actually written out.
澄清一下,输出流被缓存,所以所有的写入都会进入这个内存缓冲区。经过一段时间的安静,它们实际上被写出来了。
You write to two buffers, then after a period of inactivity they both are flushed (one after the other).
您写入两个缓冲区,然后在一段时间不活动后它们都被刷新(一个接一个)。
回答by Gemtastic
This is caused by a feature in the JVM and unless you make a hack such as the one provided by Marcus A.it's not really that easy to work around. The .flush()works in this case but the reason for this is a lot more complicated to work around.
这是由 JVM 中的一项功能引起的,除非您进行了诸如Marcus A提供的 hack 之类的 hack 。它并不是那么容易解决。在.flush()这种情况下,但这样做的原因是工程复杂多了要解决。
What is happening here?
这里发生了什么?
When you program in Java you're not telling the computer straight what to do, you're telling the JVM (Java Virtual Machine) what you would want it to do. And it will do that, but in a more efficient manner. Your code isn't exact detailed instructions, in that case you'd only need a compiler like in C and C++, the JVM takes your code as a specification list for what it's supposed to optimise and then do. This is what's happening here. Java sees that you're pushing strings into two different buffer streams. The most efficient way of doing this is by buffering all the strings you want the streams to output and then output it. This happen one stream at the time, essentially transforming your code do something like this (beware: pseudo code):
当您使用 Java 编程时,您并不是直接告诉计算机要做什么,而是在告诉 JVM(Java 虚拟机)您希望它做什么。它会做到这一点,但以更有效的方式。您的代码不是确切的详细说明,在这种情况下,您只需要像 C 和 C++ 一样的编译器,JVM 将您的代码作为应该优化然后执行的操作的规范列表。这就是这里发生的事情。Java 看到您将字符串推送到两个不同的缓冲区流中。执行此操作的最有效方法是缓冲您希望流输出的所有字符串,然后将其输出。这发生在当时的一个流中,本质上是将您的代码转换为这样的操作(注意:伪代码):
for(int i = 0; i < 5; i++) {
out.add();
err.add();
}
out.flush();
err.flush();
Because this is more efficient, this is what the JVM will do instead. Adding the .flush()in the loop will signal to the JVM that a flush needs to be done in each loop, which can't be improved with the above method. But if you for the sake of explaining how this works would have left out the loop, the JVM will reorder your code to have the printing done last, because this is more efficient.
因为这样效率更高,所以这就是 JVM 将要做的事情。添加.flush()in 循环将向 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();
This code will always be re-organized to something like this:
此代码将始终重新组织为如下所示:
System.out.println("out");*
System.err.println("err");*
System.out.println("out");*
System.err.println("err");*
System.out.flush();
System.err.flush();
Because buffering many buffers only to flush them right after takes a lot more time than to buffer all the code to be buffered and then flush it all at the same time.
因为缓冲许多缓冲区只是为了在之后立即刷新它们比缓冲所有要缓冲的代码然后同时刷新它们花费的时间要多得多。
How to solve it
如何解决
This is where code-design and architecture might come into play; you kinda don't solve this. To work around this, you have to make it more efficient to buffer print/flush, buffer print/flush than buffer all then flush. This will most likely be luring you into bad design. If it is important to you how to output it orderly, I suggest you try a different approach. For-looping with .flush()is one way to hack it, but you're still hacking the JVM's feature to re-arrange and optimize your code for you.
这就是代码设计和架构可能发挥作用的地方;你有点不解决这个问题。要解决此问题,您必须使缓冲打印/刷新、缓冲打印/刷新比缓冲全部然后刷新更有效。这很可能会引诱您进入糟糕的设计。如果对您来说如何有序输出很重要,我建议您尝试不同的方法。For 循环.flush()是破解它的一种方法,但您仍在破解 JVM 的功能,为您重新安排和优化您的代码。
*I can't verify that the buffer you added to first always will print first, but it most likely will.
*我无法验证您首先添加的缓冲区是否总是首先打印,但很可能会。
回答by Markus A.
If you are using the Eclipse console, there seem to be two different phenomena at work:
One, as described by @Gemtastic, is the JVMs handling of the streams and the other is the way Eclipse reads these streams, as mentioned by @DraganBozanovic. Since I'm using Eclipse, the elegant flush()-solution posted by @BillK, which only addresses the JVM-issue, is not sufficient.
如果您使用的是 Eclipse 控制台,似乎有两种不同的现象在起作用:
一种是@Gemtastic所描述的,是 JVM 处理流的方式,另一种是 Eclipse 读取这些流的方式,如@DraganBozanovic所提到的。由于我使用Eclipse,优雅flush()-溶液张贴@BillK,只解决了JVM的问题,是不够的。
I ended up writing myself a helper-class called EclipseToolswith the following content (and the required package declaration and imports). It's a bit of a hack but fixes both issues:
我最终为自己编写了一个辅助类,EclipseTools其中包含以下内容(以及所需的包声明和导入)。这有点黑客,但解决了两个问题:
public class EclipseTools {
private static List<OutputStream> 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<OutputStream>();
System.setErr(new PrintStream(new FixedStream(System.err)));
System.setOut(new PrintStream(new FixedStream(System.out)));
}
}
To use, just call EclipseTools.fixConsole()once in the beginning of your code.
要使用,只需EclipseTools.fixConsole()在代码的开头调用一次。
Basically, this replaces the two streams System.errand System.outwith a custom set of streams that simply forward their data to the original streams, but keep track of which stream was written to last. If the stream that is written to changes, for example a System.err.something(...)followed by a System.out.something(...), it flushes the output of the last stream and waits for 200ms to give the Eclipse console time to complete printing it.
基本上,这替换了两个流System.err和System.out一组自定义的流,这些流只是将它们的数据转发到原始流,但跟踪哪个流被写入最后。如果写入的流发生更改,例如 aSystem.err.something(...)后跟 a System.out.something(...),它会刷新最后一个流的输出并等待 200 毫秒,以便 Eclipse 控制台有时间完成打印。
Note: The 200ms are just a rough initial value. If this code reduces, but does not eliminate the problem for you, increase the delay in Thread.sleepfrom 200 to something higher until it works. Alternatively, if this delay works but impacts performance of your code (if you alternate streams often), you can try reducing it gradually until you start getting errors.
注意:200ms 只是一个粗略的初始值。如果此代码减少了,但没有为您解决问题,请将延迟Thread.sleep从 200 增加到更高,直到它起作用为止。或者,如果此延迟有效但会影响代码的性能(如果您经常交替流),您可以尝试逐渐减少它,直到开始出现错误。
回答by Dragan Bozanovic
This is a bug in Eclipse. It seems that Eclipse uses separate threads to read the content of outand errstreams without any synchronization.
这是Eclipse 中的一个错误。似乎 Eclipse 使用单独的线程来读取out和err流的内容而没有任何同步。
If you compile the class and execute it in the console (with the classic java <main class name>), the order is as expected.
如果编译类并在控制台中执行它(使用经典java <main class name>),则顺序如预期。
回答by Some guy
The two printlnstatements are handled by two different threads. The output again depends on what environment you are running the code in.
For eg, I executed the following code in IntelliJ and command-line 5 times each.
这两个println语句由两个不同的线程处理。输出再次取决于您运行代码的环境。例如,我在 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 ");
}
}
}
This resulting in the following output:
Commandline
这导致以下输出:
命令行
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
I guess different environments handles the buffers differently.
One way to see that these streams are infact handled by different threads is to add a sleepstatement in the loop. You can try varying the value that you set for the sleep and see that these are infact handled by different threads.
我猜不同的环境以不同的方式处理缓冲区。
查看这些流实际上由不同线程处理的一种方法是sleep在循环中添加一条语句。您可以尝试改变为睡眠设置的值,并查看这些值实际上由不同的线程处理。
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();
}
}
}
}
The output in this case turned out to be
在这种情况下的输出结果是
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
One way to force it to print it in the same order would be use the .flush(), which worked for me. But itseems that not everyone is getting the right results with it.
强制它以相同顺序打印的一种方法是使用.flush(),这对我有用。但似乎并不是每个人都能得到正确的结果。
The two streams handled by 2 two different threads is probably the reason why we sometimes see the ERRORmessage printed by some libraries that we use, getting printed before some print statements that we were supposed to see according to the order of execution.
由 2 个不同的线程处理的两个流可能是我们有时会看到ERROR我们使用的一些库打印的消息的原因,在我们应该根据执行顺序看到的一些打印语句之前打印。
回答by Milan Paudyal
I have used thread to print the output of System.out and System.err sequentially as:
我使用线程将 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());
}
}
回答by VonC
In Eclipse specifically, you now have, with Eclipse 2019-09synchronized standard and error output in console.
特别是在 Eclipse 中,您现在拥有Eclipse 2019-09在控制台中同步的标准和错误输出。
The Eclipse Console view currently can not ensure that mixed standard and error output is shown in the same order as it is produced by the running process.
For Java applications the launch configuration Common Tab now provides an option to merge standard and error output.
This ensures that standard and error output is shown in the same order it was produced but at the same time disables the individual coloring of error output.
Eclipse 控制台视图目前无法确保混合标准和错误输出的显示顺序与运行进程产生的顺序相同。
对于 Java 应用程序,启动配置 Common Tab 现在提供了合并标准和错误输出的选项。
这可确保标准输出和错误输出按其生成顺序显示,但同时禁用错误输出的单独着色。


