简体   繁体   English

对多个线程正确的方法到System.setErr()?

[英]Correct way to System.setErr() for multiple threads?

So I've got a Java application using a fixed thread pool to execute some code. 因此,我有一个使用固定线程池的Java应用程序来执行一些代码。 This code includes use of a third-party library that outputs to System.err. 该代码包括使用输出到System.err的第三方库。 When I had this chunk of code executing single-threaded, I "redirected" System.err to a PrintStream that ultimately ended up printing out to log4j logs. 当我让这段代码执行单线程时,我将System.err重定向到PrintStream,最终将其输出到log4j日志。 Basically it looked like this: 基本上看起来像这样:

 PrintStream oldErr = System.err;
 System.setErr(new PrintStream(/* custom logging stream here */));

 try
 {
     // do computationally expensive task here
 }
 finally
 {
     System.setErr(oldErr);
 }

That worked as expected. 如预期般运作。 The output printed to the logfiles instead of the console, and I could remove the output entirely by changing my log4j configuration. 输出打印到日志文件而不是控制台,并且可以通过更改log4j配置完全删除输出。 Perfect. 完善。

When I started to add multithreading I did a little research and came across this SO question: In a multithreaded Java program, does each thread have its own copy of System.out? 当我开始添加多线程时,我做了一些研究,并遇到了一个SO问题: 在多线程Java程序中,每个线程是否都有其自己的System.out副本? , which implied that I should do System.setErr() once before firing up my thread pool and I'd be all set. ,这意味着我应该在启动线程池之前执行一次System.setErr(),然后一切准备就绪。 Only that doesn't appear to be the case. 只是事实并非如此。 My code looks like this now: 我的代码现在看起来像这样:

 PrintStream oldErr = System.err;
 System.setErr(new PrintStream(/* custom logging stream here */));

 ExecutorService threadPool = Executors.newFixedThreadPool(maxThreadCount);

 try
 {
     // shove computationally expensive task off to a thread
     //   using threadPool.execute()
 }
 finally
 {
     System.setErr(oldErr);
 }

However, calling System.setErr() before firing up the thread pool has zero effect. 但是,在启动线程池之前调用System.setErr()的影响为零。 The threads all print their output to System.err as if I had not made the call at all. 所有线程都将其输出打印到System.err,好像我根本没有进行调用一样。 Drat! 讨厌鬼!

I also tried having the threads call System.setErr() while executing their tasks, but there were some clear race condition issues with that--intermittent output to the console, and a general feeling of dirtiness. 我还尝试在执行任务时让线程调用System.setErr(),但与此同时存在一些明显的竞争条件问题:向控制台的间歇性输出以及普遍的肮脏感。 In one test run it even looked like they deadlocked. 在一次测试运行中,甚至看起来他们陷入僵局。

Am I missing something simple? 我是否缺少简单的东西? FWIW, here are my JVM details: FWIW,这是我的JVM详细信息:

java version "1.6.0_21"
Java(TM) SE Runtime Environment (build 1.6.0_21-b07)
Java HotSpot(TM) 64-Bit Server VM (build 17.0-b17, mixed mode)

Thanks. 谢谢。

EDIT: My issue was basically solved by the accepted answer, but for sake of completeness I wanted to add that I could not solve my particular case using Futures and get() . 编辑:我的问题基本上可以通过接受的答案解决,但是出于完整性考虑,我想补充一点,我无法使用Futures和get()解决我的特殊情况。 My individual tasks consume a lot of RAM but vary in duration, so I had to go with a small blocking queue as outlined in the answer to Java: ExecutorService that blocks on submission after a certain queue size . 我的单个任务占用大量RAM,但持续时间各不相同,因此我不得不使用一个小的阻塞队列,如Java答案:ExecutorService所述,该阻塞队列在一定队列大小后提交时会阻塞 I basically fell into the trap of thinking that the defaults in newFixedThreadPool() would work for my situation when in reality they did not, and Brian's answer helped expose those faulty assumptions. 我基本上陷入了这样一种陷阱:认为newFixedThreadPool()中的默认值在我的情况下不起作用,而实际上却不行,而Brian的回答有助于揭露那些错误的假设。 Thanks Brian! 谢谢布莱恩!

The following SSCCE does exactly what you want and are expecting: 以下SSCCE完全可以满足您的期望和期望:

public class App
{
    public static void main(String[] args) throws FileNotFoundException, InterruptedException, ExecutionException 
    {
        PrintStream oldErr = System.err;
        System.setErr(new PrintStream(new File("test")));

        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        List<Future<?>> fList = new LinkedList<Future<?>>();
        for (int i = 0; i < 5; i++)
        {
            fList.add(threadPool.submit(new Runnable() {

                @Override
                public void run()
                {
                    System.err.println("This is to the err stream");
                }

            }));
        }

        for (Future<?> f : fList)
        {
            f.get();
        }

        threadPool.shutdown();

        System.setErr(oldErr);
        System.err.println("This is to the err stream");
    }
}

After running this example, the file "test" contains 5 lines, each saying "This is to the err stream" and at the end one additional line is printed to the console. 运行此示例后,文件“ test”包含5行,每行显示“ This is to err stream”,最后,在控制台上又打印了一行。

The code snippet you show is calling System.setErr(oldErr); 您显示的代码片段调用System.setErr(oldErr); in the finally block ... have you waited for all your tasks to complete in that try block? finally块中...您是否已在该try块中等待所有任务完成?

If not it would explain the statement you make in the comments that "I would see fragments of output printed to System.err and fragments printed to my logfile" ... that's pretty much what you'd expect to happen. 如果不是这样,它将解释您在注释中所做的声明,即“我将看到输出的片段打印到System.err,而片段打印到我的日志文件” ……这几乎是您期望发生的事情。 .submit() is a non-blocking call that returns a Future ; .submit()是一个非阻塞调用,它返回一个Future you're resetting the stream while your tasks are running. 您正在运行任务时重置流。

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

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