简体   繁体   English

在多线程 Java 程序中,是否每个线程都有自己的 System.out 副本?

[英]In a multithreaded Java program, does each thread have its own copy of System.out?

I'm writing a multithreaded Java program where each thread potentially needs its standard output redirected to a separate file.我正在编写一个多线程 Java 程序,其中每个线程可能需要将其标准 output 重定向到一个单独的文件。 Each thread would have its own file.每个线程都有自己的文件。 Is it possible to redirect System.out on a "per-thread" basis or are changes to System.out global across all threads?是否可以在“每个线程”的基础上重定向 System.out,或者对所有线程的 System.out 进行全局更改?

Is it possible to redirect System.out on a "per-thread" basis是否可以在“每线程”的基础上重定向 System.out

No it is not possible.不,这是不可能的。 System.out is static and there is one per JVM that is loaded as part of the system classloader when the JVM initially boots. System.out是 static,每个 JVM 都有一个在 JVM 最初启动时作为系统类加载器的一部分加载。 Although of course using proper logging calls per-thread is recommend, I assume there are reasons why you can't do this.虽然当然建议每个线程使用适当的日志记录调用,但我认为您有一些原因不能这样做。 Probably a 3rd party library or other code is what is using System.out in this manner.可能是第 3 方库或其他代码以这种方式使用System.out

One thing you could do (as a radical suggestion) is to make your own PrintStream that delegates to a ThreadLocal<PrintStream> .你可以做的一件事(作为一个激进的建议)是制作你自己的PrintStream委托给ThreadLocal<PrintStream> But you will need to @Override the appropriate methods called by your application to get it to work per-thread.但是您需要@Override应用程序调用的适当方法才能使其按线程工作。

Lastly, if you are asking this because you are worried about concurrency, System.out is a PrintStream so it is already synchronized under the covers and can be used safely by multiple threads.最后,如果您是因为担心并发而问这个问题,那么System.out是一个PrintStream ,因此它已经在幕后synchronized并且可以被多个线程安全地使用。

Is it possible to redirect System.out on a "per-thread" basis是否可以在“每线程”的基础上重定向 System.out

Some developers from Maia Company have provided a public implementation of a PrintStream that provides one " STDOUT " per thread in this article: " Thread Specific System.out ". Maia 公司的一些开发人员提供了 PrintStream 的公共实现,在本文中为每个线程提供一个“ STDOUT ”:“ Thread Specific System.out ”。

In their implementation they override only write methods, flush, close and checkError.在他们的实现中,他们只覆盖写入方法、flush、close 和 checkError。 It seems to be enough in their case.对他们来说这似乎就足够了。

They did not " need to @Override all of the methods called to get it to work per-thread " as @Gray stated in his answer.正如@Gray在他的回答中所述,他们并不需要@Override 所有被调用的方法以使其按线程工作”。


NOTA:注意:

Please find below the original code from Maia.请在下面找到来自 Maia 的原始代码。

I found it here on the wayback machine.我在回程机上找到它。 The original page was removed from the website of Maia.原始页面已从 Maia 网站上删除。 I reproduce it here for the reader's curiosity.为了读者的好奇心,我在这里转载了它。 I do not provide any support for this code.我不为此代码提供任何支持。


Main.java主要.java

Creates a ThreadPrintStream, installs it as System.out, and creates and starts 10 threads.创建一个 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 StreamText.java

A simple Runnable for each thread that opens a file for the thread's output and installs it into the ThreadPrintStream.每个线程的简单 Runnable 为线程的 output 打开一个文件并将其安装到 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 ThreadPrintStream.java

Extends java.io.PrintStream.扩展 java.io.PrintStream。 An object of ThreadPrintStream replaces the normal System.out and maintains a separate java.io.PrintStream for each thread. ThreadPrintStream 的 object 替换了正常的 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<PrintStream> out;

  private ThreadPrintStream() {
    super(new ByteArrayOutputStream(0));
    out = new ThreadLocal<PrintStream>();
  }

  /** 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(); }
}

You are right but not in the way you think.你是对的,但不是你想的那样。 When a thread uses当一个线程使用

System.out.println();

It takes a copy of the reference System.out , but not a copy of the object this references.它获取引用System.out的副本,但不获取此引用的 object 的副本。

This means all threads will normally see the same object for writing to output.这意味着所有线程通常会看到相同的 object 以写入 output。

Note: This fields in not thread safe and if you call System.setOut(PrintStream) If you use this there is a potential, undesirable race condition where different threads to have different local copies of System.out.注意:此字段不是线程安全的,如果您调用System.setOut(PrintStream)如果您使用它,则存在潜在的、不希望的竞争条件,即不同的线程具有 System.out 的不同本地副本。 This cannot be used to solve this question.这不能用来解决这个问题。

Is it possible to redirect System.out on a "per-thread" basis是否可以在“每线程”的基础上重定向 System.out

You can do this by replacing System.out with your own implementation which is thread specific.您可以通过将 System.out 替换为您自己的特定于线程的实现来做到这一点。 ie a sub class of PrintStream.即 PrintStream 的子 class。 I have done this for logging where I wanted each thread's output to be consistent and not interleaved.我这样做是为了记录我希望每个线程的 output 保持一致且不交错的位置。 eg Imagine printing two stack traces in two threads at the same time.例如想象一下同时在两个线程中打印两个堆栈跟踪。 ;) ;)

System.out is static, and therefore the same instance is shared between all threads. System.out是 static,因此所有线程共享同一个实例。

Is it possible to redirect System.out on a "per-thread" basis是否可以在“每线程”的基础上重定向 System.out

You can redirect them all to your delegate which will be responsible for 'per-thread' logic.您可以将它们全部重定向到您的代表,该代表将负责“每线程”逻辑。

Here is example of parallel JBehave tests having theirs own file output. 这里是并行 JBehave 测试的例子,它们有自己的文件 output。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM