Java System.out.println和System.err.println出现故障

Java System.out.println和System.err.println出现故障,java,console,system,Java,Console,System,我的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和err。这是为什么?它们是不同的流,在不同的

我的
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
err
。这是为什么?

它们是不同的流,在不同的时间冲洗

如果你把

System.out.flush();
System.err.flush();
在您的循环中,它将按预期工作

为了澄清这一点,输出流是缓存的,所以所有的写操作都会进入这个内存缓冲区。经过一段时间的安静之后,它们实际上被写出来了


您写入两个缓冲区,然后在一段时间不活动后,它们都被刷新(一个接一个)。

如果您使用Eclipse控制台,工作中似乎有两种不同的现象:
如所述,一种是JVM处理流的方式,另一种是Eclipse读取这些流的方式,如所述。因为我使用的是Eclipse,所以发布的优雅的
flush()
-解决方案(仅解决JVM问题)是不够的

最后我自己编写了一个名为
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)));
    }
}
公共类日食工具{
私有静态列表流=null;
私有静态OutputStream lastStream=null;
私有静态类FixedStream扩展OutputStream{
私有最终输出流目标;
公共固定流(OutputStream originalStream){
目标=原始流;
添加(此项);
}
@凌驾
公共无效写入(int b)引发IOException{
如果(lastStream!=此)交换();
目标。编写(b);
}
@凌驾
公共无效写入(字节[]b)引发IOException{
如果(lastStream!=此)交换();
目标。编写(b);
}
@凌驾
公共无效写入(字节[]b,int off,int len)引发IOException{
如果(lastStream!=此)交换();
目标。注销(b、注销、len);
}
私有void swap()引发IOException{
if(lastStream!=null){
lastStream.flush();
试试{Thread.sleep(200);}catch(InterruptedException e){}
}
lastStream=这个;
}
@重写公共void close()引发IOException{target.close();}
@重写公共void flush()引发IOException{target.flush();}
}
/**
*将200ms延迟插入System.err或System.out OutputStreams
*每次输出从一个切换到另一个时。这会防止
*Eclipse控制台不显示两个数据流的输出
*这个函数只需要调用一次。
*/
公共静态void fixConsole(){
如果(streams!=null)返回;
streams=新的ArrayList();
System.setErr(新的PrintStream(新的FixedStream(System.err));
系统放样(新打印流(新固定流(系统放样));
}
}
要使用,只需在代码开头调用一次
EclipseTools.fixConsole()

基本上,这将用一组自定义流取代两个流
System.err
System.out
,这些流只需将其数据转发到原始流,但要跟踪最后写入的流。如果写入的流发生更改,例如
System.err.something(…)
后跟
System.out.something(…)
,它将刷新最后一个流的输出,并等待200ms,以便Eclipse控制台有时间完成打印


注意:200ms只是一个粗略的初始值。如果此代码减少了,但没有为您消除问题,请将
Thread.sleep
中的延迟从200增加到更高的值,直到它正常工作。或者,如果这种延迟有效,但会影响代码的性能(如果您经常交替使用流),您可以尝试逐渐减少这种延迟,直到您开始出错。

这是由JVM中的某个功能造成的,除非您进行类似于Marcus a提供的黑客攻击。这并不是很容易解决的问题。
.flush()
在这种情况下可以工作,但其原因要复杂得多

这里发生了什么? 当你用Java编程时,你不是直接告诉计算机该做什么,而是告诉JVM(Java虚拟机)你希望它做什么。它会做到这一点,但会以更有效的方式。你的代码不是精确的详细说明,在这种情况下,你只需要像C和C++一样的编译器,JVM将你的代码作为一个规范列表,用于优化它然后做。strong>这就是这里发生的事情。Java看到您正在将字符串推送到两个不同的缓冲流中。实现这一点的最有效方法是缓冲所有希望流输出的字符串,然后输出它。这种情况一次只发生一个流,本质上是转换代码,并执行如下操作(注意:伪代码):

此代码将始终按如下方式重新组织:

System.out.println("out");*
System.err.println("err");*
System.out.println("out");*
System.err.println("err");*
System.out.flush();
System.err.flush();
因为缓冲多个缓冲区只是在紧接着刷新它们要比缓冲所有要缓冲的代码然后同时刷新它们要花费更多的时间

如何解决 这是代码设计和架构可能发挥作用的地方;你解决不了这个问题。为了解决这个问题,您必须使缓冲打印/刷新、缓冲打印/刷新比缓冲全部然后刷新更有效。这很可能会诱使你进入糟糕的设计。如果如何有序地输出它对您很重要,我建议您尝试另一种方法。使用
.flush()
循环是一种破解方法,但您仍然在破解JVM的功能,以便为您重新安排和优化代码

*
我无法验证您添加到f的缓冲区
for(int i = 0; i < 5; i++) {
    out.add();
    err.add();
}
out.flush();
err.flush();
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();
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
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 
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 
    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());
        }
    }