首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >System.out.println和System.err.println无序

System.out.println和System.err.println无序
EN

Stack Overflow用户
提问于 2009-12-11 03:20:58
回答 7查看 22.7K关注 0票数 55

我的System.out.println()System.err.println()调用没有按我发出的顺序打印到控制台。

代码语言:javascript
复制
public static void main(String[] args) {
    for (int i = 0; i < 5; i++) {
        System.out.println("out");
        System.err.println("err");
    }
}

这会产生以下结果:

代码语言:javascript
复制
out
out
out
out
out
err
err
err
err
err

而不是交替使用outerr。为什么会这样呢?

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2009-12-11 03:23:11

它们是不同的流,并且在不同的时间刷新。

如果你把

代码语言:javascript
复制
System.out.flush();
System.err.flush();

在您的循环中,它将按预期工作。

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

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

票数 38
EN

Stack Overflow用户

发布于 2015-07-07 17:15:36

这是由JVM中的一个特性引起的,除非您像Marcus A.提供的那样进行黑客攻击,否则解决起来并不是那么容易。.flush()在这种情况下可以工作,但其原因要复杂得多。

这是怎么回事?

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

代码语言:javascript
复制
for(int i = 0; i < 5; i++) {
    out.add();
    err.add();
}
out.flush();
err.flush();

因为这样效率更高,所以这就是JVM要做的事情。在循环中添加.flush()将通知JVM需要在每个循环中进行刷新,这是上面的方法无法改进的。但是,如果您只是为了解释它是如何工作的,那么JVM将对您的代码进行重新排序,以便最后完成打印,因为这样效率更高。

代码语言:javascript
复制
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();

此代码将始终重新组织为类似以下内容:

代码语言:javascript
复制
System.out.println("out");*
System.err.println("err");*
System.out.println("out");*
System.err.println("err");*
System.out.flush();
System.err.flush();

因为缓冲许多缓冲区只是为了在之后立即刷新它们,所以比起缓冲所有要缓冲的代码然后同时刷新它们,需要花费更多的时间。

如何解决这个问题

这就是代码设计和架构可能发挥作用的地方;你可能没有解决这个问题。要解决这个问题,你必须使缓冲打印/刷新,缓冲打印/刷新比缓冲所有然后刷新更有效。这很可能会引诱你进入糟糕的设计。如果如何有序地输出对你来说很重要,我建议你尝试一种不同的方法。使用.flush()的for循环是破解它的一种方法,但是您仍然在破解JVM虚拟机的特性来重新安排和优化您的代码。

*我无法验证您添加到first的缓冲区是否总是首先打印,但它很可能会。

票数 30
EN

Stack Overflow用户

发布于 2014-05-17 09:42:08

如果您正在使用Eclipse控制台,那么似乎有两种不同的现象在起作用:

一个是@Gemtastic对流的JVM处理,另一个是@DraganBozanovic提到的读取这些流的方式。因为我使用的是Eclipse,所以@BillK发布的优雅的flush()-solution是不够的,它只解决了JVM问题。

最后,我编写了一个名为EclipseTools的助手类,其中包含以下内容(以及所需的包声明和导入)。这是一个小技巧,但解决了这两个问题:

代码语言:javascript
复制
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)));
    }
}

要使用,只需在代码开头调用一次EclipseTools.fixConsole()即可。

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

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

票数 10
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1883321

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档