当前位置: 首页 > 知识库问答 >
问题:

在多线程Java程序中,每个线程是否都有自己的系统副本。出来

拓拔元徽
2023-03-14

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

共有3个答案

钱均
2023-03-14

你是对的,但不是你想的那样。当一个线程使用

System.out.println();

它获取引用System.out的副本,但不获取此引用的对象的副本。

这意味着所有线程通常会看到同一个对象来写入输出。

注意:如果调用系统,则此字段将处于非线程安全状态。setOut(PrintStream)如果使用此选项,则可能会出现一种不希望出现的争用情况,即不同的线程需要具有不同的系统本地副本。出来这不能用来解决这个问题。

是否可以重定向系统。以“每线程”为基础

您可以通过更换系统来实现这一点。使用自己的特定于线程的实现。i、 e.PrintStream的一个子类。我这样做是为了记录,我希望每个线程的输出是一致的,而不是交错的。e、 g.想象一下同时在两个线程中打印两个堆栈轨迹

沈实
2023-03-14

是否可以重定向系统。以“每线程”为基础

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

在它们的实现中,它们只覆盖write方法、flush、close和checkError。对他们来说,这似乎足够了。

他们不需要像@Gray在回答中所说的那样“覆盖所有调用的方法,以使每个线程都能工作”。

NOTA:

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

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

Main.java

创建ThreadPrintStream,并将其作为系统安装。然后创建并启动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());
    }
  }
}

流文本。JAVA

一个简单的Runnable for each thread,为线程的输出打开一个文件,并将其安装到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。伊奥。打印流。ThreadPrintStream的一个对象替换了正常的系统。开发并维护一个单独的java。伊奥。每个线程的打印流。

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(); }
}
张博涛
2023-03-14

是否可以重定向系统。以“每线程”为基础

不,这是不可能的。System.out是静态的,当JVM最初引导时,每个JVM都有一个作为系统类加载器的一部分加载。虽然当然建议每个线程使用适当的日志调用,但我认为有一些原因使您不能这样做。可能是第三方库或其他代码以这种方式使用System.out

你可以做的一件事(作为一个激进的建议)是制作你自己的PrintStream,它委托给一个线程本地

最后,如果您因为担心并发而问这个问题,那么System.out是一个PrintStream,因此它已经被同步了,并且可以被多个线程安全地使用。

 类似资料:
  • 问题内容: 我正在编写一个多线程Java程序,其中每个线程可能都需要将其标准输出重定向到一个单独的文件。每个线程都有其自己的文件。是否可以在“每个线程”的基础上重定向System.out或在所有线程上全局更改System.out? 问题答案: 是否可以基于“每线程”重定向System.out 不,这是不可能的。 是静态的,并且在JVM最初启动时,每个JVM都会作为系统类加载器的一部分进行加载。尽管

  • 我试图理解这句话的含义: 每个Java虚拟机线程都有自己的pc(程序计数器)寄存器。在任何时候,每个Java虚拟机线程都在执行单个方法的代码,即该线程的当前方法(§2.6)。 https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.5.1 我假设JVM线程像任何其他线程一样工作——每次调度线程运行(比如Linux内

  • 我在试着理解这句话的意思: 每个Java虚拟机线程都有自己的pc(程序计数器)寄存器。在任何一点上,每个Java虚拟机线程都在执行单个方法的代码,即该线程的当前方法(§2.6)。 https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.5.1 我假设JVM线程的工作方式与任何其他线程一样--每次调度线程运行时(比方

  • 我有多个带有注释的组件,我看到Spring一次只启动一个组件,即使它们被安排在同一时间运行。

  • 我正在尝试设置一个Java程序,其中每个线程都可以使用自己的代理。 现在我只找到了一种全局设置代理的方法。(http://docs.oracle.com/javase/6/docs/technotes/guides/net/proxies.html) 如前所述,这些设置会影响使用这些选项调用的VM的整个生命周期内的所有http连接。然而,使用该系统是可能的。setProperty()方法,使其具有

  • 当我运行一个多线程的java程序时,在执行ps -eF | grep program-name时,我只看到一个进程。作为该进程的一部分运行的线程是子进程。在调度方面,操作系统对待单线程和多线程进程是否有所不同?