在multithreadingJava程序中,每个线程都有自己的System.out副本吗?

我正在编写一个multithreadingJava程序,其中每个线程可能需要将其标准输出重定向到单独的文件。 每个线程都有自己的文件。 是否可以在“每个线程”的基础上重定向System.out,还是在所有线程中更改为System.out全局?

是否可以在“每个线程”的基础上重定向System.out

不,这是不可能的。 System.out是静态的,当JVM最初启动时,每个JVM都有一个作为系统类加载器的一部分加载。 虽然建议每个线程使用正确的日志记录调用,但我认为有理由不能这样做。 可能是第三方库或其他代码就是以这种方式使用System.out

你可以做的一件事(作为一个激进的建议)是让你自己的PrintStream委托给一个ThreadLocal 。 但是,您需要@Override应用程序调用的相应方法才能使其按线程运行。

最后,如果你因为担心并发而问这个问题,那么System.out就是一个PrintStream所以它已经在封面下synchronized ,可以被多个线程安全地使用。

是否可以在“每个线程”的基础上重定向System.out

Maia公司的一些开发人员提供了一个PrintStream的公共实现,它在本文中为每个线程提供一个“ STDOUT ”:“ Thread Specific System.out ”。

在它们的实现中,它们只覆盖write方法,flush,close和checkError。 在他们的情况下似乎已经足够了。

正如@Gray在他的回答中所述,他们并没有需要@Override所有被调用的方法让它按照每个线程运行 ”。


NOTA:

请在下面找到Maia的原始代码。

我在回程机器上找到了它。 原始页面已从Maia的网站上删除。 我在这里重现它是为了读者的好奇心。 我不对此代码提供任何支持。


Main.java

创建一个ThreadPrintStream,将其安装为System.out,并创建并启动10个线程。

 public class Main { public static void main(String[] args) { // Call replaceSystemOut which replaces the // normal System.out with a ThreadPrintStream. ThreadPrintStream.replaceSystemOut(); // Create and start 10 different threads. Each thread // will create its own PrintStream and install it into // the ThreadPrintStream and then write three messages // to System.out. for (int i = 0; i < 10; i++) { Thread thread = new Thread(new StreamText()); thread.start(); // Report to the console that a new thread was started. System.out.println("Created and started " + thread.getName()); } } } 

StreamText.java

每个线程的简单Runnable,用于打开线程输出的文件并将其安装到ThreadPrintStream中。

 import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.PrintStream; /** A small test class that sets System.out for the currently executing * thread to a text file and writes three messages to System.out. */ public class StreamText implements Runnable { @Override public void run() { try { // Create a text file where System.out.println() // will send its data for this thread. String name = Thread.currentThread().getName(); FileOutputStream fos = new FileOutputStream(name + ".txt"); // Create a PrintStream that will write to the new file. PrintStream stream = new PrintStream(new BufferedOutputStream(fos)); // Install the PrintStream to be used as System.out for this thread. ((ThreadPrintStream)System.out).setThreadOut(stream); // Output three messages to System.out. System.out.println(name + ": first message"); System.out.println("This is the second message from " + name); System.out.println(name + ": 3rd message"); // Close System.out for this thread which will // flush and close this thread's text file. System.out.close(); } catch (Exception ex) { ex.printStackTrace(); } } } 

ThreadPrintStream.java

扩展java.io.PrintStream。 ThreadPrintStream的一个对象替换了普通的System.out,并为每个线程维护一个单独的java.io.PrintStream。

 import java.io.ByteArrayOutputStream; import java.io.PrintStream; /** A ThreadPrintStream replaces the normal System.out and ensures * that output to System.out goes to a different PrintStream for * each thread. It does this by using ThreadLocal to maintain a * PrintStream for each thread. */ public class ThreadPrintStream extends PrintStream { /** Changes System.out to a ThreadPrintStream which will * send output to a separate file for each thread. */ public static void replaceSystemOut() { // Save the existing System.out PrintStream console = System.out; // Create a ThreadPrintStream and install it as System.out ThreadPrintStream threadOut = new ThreadPrintStream(); System.setOut(threadOut); // Use the original System.out as the current thread's System.out threadOut.setThreadOut(console); } /** Thread specific storage to hold a PrintStream for each thread */ private ThreadLocal out; private ThreadPrintStream() { super(new ByteArrayOutputStream(0)); out = new ThreadLocal(); } /** Sets the PrintStream for the currently executing thread. */ public void setThreadOut(PrintStream out) { this.out.set(out); } /** Returns the PrintStream for the currently executing thread. */ public PrintStream getThreadOut() { return this.out.get(); } @Override public boolean checkError() { return getThreadOut().checkError(); } @Override public void write(byte[] buf, int off, int len) { getThreadOut().write(buf, off, len); } @Override public void write(int b) { getThreadOut().write(b); } @Override public void flush() { getThreadOut().flush(); } @Override public void close() { getThreadOut().close(); } } 

你是对的但不是你想的那样。 线程使用时

 System.out.println(); 

它需要参考 System.out的副本,但不是此引用的对象的副本。

这意味着所有线程通常会看到相同的对象以写入输出。

注意:此字段不是线程安全的,如果您调用System.setOut(PrintStream)如果您使用此字段,则存在潜在的,不合需要的竞争条件,其中不同的线程具有不同的System.out本地副本。 这不能用来解决这个问题。

是否可以在“每个线程”的基础上重定向System.out

您可以通过将System.out替换为您自己的特定于线程的实现来完成此操作。 即PrintStream的子类。 我已经做了这个记录,我希望每个线程的输出是一致的而不是交错的。 例如,想象一下在两个线程中同时打印两个堆栈跟踪。 ;)

System.out是静态的,因此在所有线程之间共享相同的实例。

是否可以在“每个线程”的基础上重定向System.out

您可以将它们全部重定向到您的委托,该委托将负责“每线程”逻辑。

以下是具有自己文件输出的并行JBehave测试示例。