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

5

14

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 possible to redirect System.out on a "per-thread" basis or are changes to System.out global across all threads?

Sheepdog answered 4/4, 2012 at 16:16 Comment(1)
You might be able to return a different PrintStream object using AspectJSasnett
E
27

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

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

One thing you could do (as a radical suggestion) is to make your own PrintStream that delegates to a ThreadLocal<PrintStream>. But you will need to @Override the appropriate methods called by your application to get it to work per-thread.

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.

Eckenrode answered 4/4, 2012 at 16:18 Comment(7)
I'm currently looking into naming each thread, and making a custom PrintStream that takes a Map<Thread, OutputStream>. When the custom printstream receives a signal to print something to the screen, it uses Thread.currentThread to get the current Thread object and looks it up in the map. If the thread is a key in the map, the output is written to the corresponding OutputStream, otherwise it goes to a default output.Sheepdog
You can use java.util.logging (or another logging framework) with an appropriate formatter in order to log the thread name. I would strongly recommend this approach over using System.out.Frye
I need to redirect the standard output to a variable location based on which thread is running. The threads don't output standard "log" error messages. I looked at Java Logging and I don't think it's appropriate for my issue.Sheepdog
"System.out is static and there is one per ClassLoader" is misleading at best: the java.lang.System class can only be loaded by the bootstrap class loader (along with all other java.lang classes).Inwardness
But it can be set as well @BrettKail. How is it misleading?Eckenrode
@Eckenrode It can be set, but there is only one static variable per JVM, not one per ClassLoader. Your statement caused confusion in this question, which is why I downvoted (if you can clarify what you were trying to say, we can edit your answer, and I'll remove the downvote).Inwardness
It's not misleading @BrettKail. It's just wrong. I tested it and verified it is loaded as part of the system classloader and multiple classloaders have the same reference. Thanks.Eckenrode
K
9

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<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(); }
}
Kapok answered 20/3, 2013 at 14:29 Comment(3)
Seems this link is dead, but you can find a similar implementation in org.apache.geronimo.gshell.support.gshell-io.SystemOutputHijacker Maven package and source code browserAver
@Kapok Do you have a working link (or access to a downloadable Jar file) to the said solution from Maia Company? As stated by ArneBrasseur the original link is no longer works and the project looks interesting.Inseparable
@Inseparable Please see my answer (https://mcmap.net/q/473322/-in-a-multithreaded-java-program-does-each-thread-have-its-own-copy-of-system-out) update.Kapok
F
4

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.

This means all threads will normally see the same object for writing to 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. This cannot be used to solve this question.

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

You can do this by replacing System.out with your own implementation which is thread specific. i.e. a sub class of PrintStream. I have done this for logging where I wanted each thread's output to be consistent and not interleaved. e.g. Imagine printing two stack traces in two threads at the same time. ;)

Fawkes answered 4/4, 2012 at 16:28 Comment(4)
You can control it my setting it before starting any more threads, otherwise you have to hope it doesn't matter. What you do you mean Boy this is misleading ?Fawkes
There is only a single System.out. What you are suggesting is that you change System.out depending on the thread, which is not the same as having multiple System.out. Your suggestion is analogous to saying that having 8 CD-ROMs and a single CD-ROM drive is equivalent to having 8 CD-ROM drives because you can switch out the CD when needed. Can CDs be switched out? Yes. Is it the same as having 8 CD drives? no.Sheepdog
I am suggest you change the System.out to be a component which does something different on a per thread basis. This is not the same as having multiple System.out values. My comment was about the thread safety of System.out which allows multiple threads to see different values due to a race condition, something which you cannot use and would only be a problem. I am suggesting having an 8 CD changer which appears as one drive to the user e.g. with 8 sub-directories.Fawkes
@Eckenrode I have reworded this. Thank you for your feedback.Fawkes
A
3

System.out is static, and therefore the same instance is shared between all threads.

Alliterate answered 4/4, 2012 at 16:18 Comment(0)
F
0

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

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.

Frontogenesis answered 7/6, 2016 at 14:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.