gpt4 book ai didi

Java PrintStream 重定向行为异常

转载 作者:行者123 更新时间:2023-11-30 02:27:58 26 4
gpt4 key购买 nike

当我使用 System.out.println 打印日志消息时,我正在编写一个基本的服务器程序。我编写了一个基本的类文件,使用它来写入日志。如果我写以下内容:

System.out.println("Hello, world!");
System.out.println("Goodbye, world");

期望的输出是:

Log message - Hello, world!
Log message - Goodbye, world!

最终发生的情况与期望的输出不匹配。相反,它输出到以下内容。

Log message - Hello, world!
Goodbye, world!

主要方法的代码:

public static void main(String[] args){
LogManager.start();
System.out.println("Hello, world!");
System.out.println("Goodbye, world!");
LogManager.stop();
}

LogManager 类切换打印输出的默认 PrintStream,并保留旧版本的副本以打印日志消息。但是,“Log message - ”并不总是带有前缀。不过,当每次 println 调用之间 hibernate 2000 毫秒时,输出如下所示。

Log message - Hello, world!
Log message - Goodbye, world!Log message -

LogManager的代码如下。

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;

public class LogManager implements Runnable{

private final PrintStream ps;
private final OutputStream out;
private static boolean cont = true;

public static void start(){
OutputStream stdout = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(stdout);
Thread th = new Thread(new LogManager(System.out, stdout));
System.setOut(ps);
th.start();
}

public static void stop(){
cont = false;
}

public LogManager(PrintStream std, OutputStream out){
this.ps = std;
this.out = out;
}

@Override
public void run() {
ByteArrayOutputStream baos = (ByteArrayOutputStream) out;
while(true){
if(!cont) return;
byte[] bytes = baos.toByteArray();
if(bytes.length > 0){
baos.reset();
ps.print("Log message - " + new String(bytes));
}
}
}
}

有人可以指出我做错了什么吗,我们将不胜感激。我想远离库,因为我想将 JAR 大小保持在最小,不需要包含额外的包,尽管主要是因为我知道我没有使用任何其他人的库来实现我正在做的事情。

最佳答案

您有一些竞争条件。

首先,一旦 stop() 完成,您的程序就会结束。当发生这种情况时,LogManager 线程可能有机会看到已写入的新字节:

  1. 主线程写入“再见,世界\n”
  2. 主线程集cont = false
  3. LogManager 线程发现 cont == false 并在有机会写入其字节之前停止。

此外,您还可以使用 baos.toByteArray(),然后作为单独的操作执行 baos.reset()。如果有人在两个操作之间写一些东西会发生什么?它们不会反射(reflect)在 bytes 变量中,但 reset() 会删除它们。

要解决第一个问题,您可以在返回之前进行最后一次检查。换句话说,如果您将整个 toByteArray()/reset()/println 位重构为方法 readAndPrint(),则 return 语句将变为:

if (!cont) {
readAndPrint(); // one last read to empty the buffer
return;
}

要解决第二个问题,您应该在锁定 boas 的同时执行 toByteArray()reset()(这将还锁定对该流的写入,因为 ByteArrayOutputStream 中的所有读取和写入都是同步的)。这将确保在您执行这两个操作时没有其他人可以写入。

byte[] bytes;
synchronized (baos) {
bytes = baos.toByteArray();
baos.reset();
}
if (bytes.length > ) { ...

此外,您应该将 cont 字段设置为 volatile ,以便一个线程中的写入始终可以在另一个线程中看到。

请注意,上述内容仍会让您参加一些比赛。例如,如果您有两个“主”线程,您可以想象这样一种情况:其中一个线程调用 stop(),而另一个线程仍在尝试打印消息。解决方案是以某种方式对其进行协调,以便在您调用 stop() 时,所有线程都已完成其日志记录。

多线程是一个非常复杂和微妙的主题,很难通过实验来学习。如果您还没有这样做,我强烈建议您阅读一本书或深入的教程,以充分掌握问题和解决问题的方法。

最后,您没有询问输出中的奇怪换行符,但它们可能是由于您使用正在刷新的 PrintStream(从而将其内容写入 BAOS)作为信号用于打印前缀,而不是在字节缓冲区中看到换行符。如果刷新发生在写入换行符之前,您将看到所看到的行为。

关于Java PrintStream 重定向行为异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45124527/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com