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

use*_*361 13 java io multithreading outputstream

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

Gra*_*ray 23

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

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

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

最后,如果你问这个是因为你担心的并发,System.outPrintStream所以它已经是synchronized在幕后,并可以被多个线程安全地使用.


Ste*_*han 8

是否可以在"每个线程"的基础上重定向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());
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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<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(); }
}
Run Code Online (Sandbox Code Playgroud)

  • 似乎这个链接已经死了,但您可以在`org.apache.geronimo.gshell.support.gshell-io.SystemOutputHijacker` [Maven包](https://mvnrepository.com/artifact/org.apache)中找到类似的实现. geronimo.gshell.support/gshell-io/1.0-alpha-2)和[源代码浏览器](http://grepcode.com/file/repo1.maven.org/maven2/org.apache.servicemix.kernel.gshell /org.apache.servicemix.kernel.gshell.core/1.1.0/org/apache/geronimo/gshell/io/SystemOutputHijacker.java) (3认同)