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

前端 未结 5 1547
我寻月下人不归
我寻月下人不归 2020-12-06 10:36

I\'m writing a multithreaded Java program where each thread potentially needs its standard output redirected to a separate file. Each thread would have its own file. Is it p

5条回答
  •  半阙折子戏
    2020-12-06 11:19

    Is it possible to redirect System.out on a "per-thread" basis

    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".

    In their implementation they override only write methods, flush, close and 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.


    NOTA:

    Please find below the original code from Maia.

    I found it here on the wayback machine. The original page was removed from the website of Maia. I reproduce it here for the reader's curiosity. I do not provide any support for this code.


    Main.java

    Creates a ThreadPrintStream, installs it as System.out, and creates and starts 10 threads.

    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

    A simple Runnable for each thread that opens a file for the thread’s output and installs it into the 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

    Extends java.io.PrintStream. An object of ThreadPrintStream replaces the normal System.out and maintains a separate java.io.PrintStream for each thread.

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

提交回复
热议问题