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

时间:2013-12-28 00:00:58

标签: java multithreading io outputstream

所以我得到了一个使用固定线程池来执行某些代码的Java应用程序。此代码包括使用输出到System.err的第三方库。当我有这块代码执行单线程时,我重定向" System.err到PrintStream最终最终打印到log4j日志。基本上它看起来像这样:

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

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

按预期工作。输出打印到日志文件而不是控制台,我可以通过更改我的log4j配置完全删除输出。完美。

当我开始添加多线程时,我做了一些研究并遇到了这个问题:In a multithreaded Java program, does each thread have its own copy of System.out?,这意味着我应该在启动我的线程池之前执行一次System.setErr(),然后我就会这样做。 d全部设定。只有这似乎并非如此。我的代码现在看起来像这样:

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

但是,在启动线程池之前调用System.setErr()没有任何影响。所有线程都将其输出打印到System.err,就像我根本没有打过电话一样。 DRAT!

我还尝试让线程在执行任务时调用System.setErr(),但是有一些明显的竞争条件问题 - 间歇性输出到控制台,以及一般的肮脏感觉。在一次测试中,它甚至看起来像是僵局了。

我错过了一些简单的东西吗? 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)

感谢。

编辑:我的问题基本上是通过接受的答案解决的,但为了完整起见,我想补充一点,我无法使用期货和get()来解决我的具体案例。我的个人任务消耗了大量的RAM但持续时间不同,因此我必须使用Java: ExecutorService that blocks on submission after a certain queue size答案中概述的小阻塞队列。我基本上陷入了认为newFixedThreadPool()中的默认值对我的情况有效的陷阱,而实际上他们没有,而Brian的回答帮助揭露了那些错误的假设。谢谢Brian!

1 个答案:

答案 0 :(得分:1)

以下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");
    }
}

运行此示例后,文件“test”包含5行,每行代表“这是对错误的流”,最后在控制台上打印一行。

您显示的代码段是在System.setErr(oldErr);块中调用finally ...您是否等待在try块中完成所有任务?

如果没有,它会解释你在评论中所做的陈述“我会看到打印到System.err的片段和打印到我的日志文件的片段” ...这几乎就是你期待发生。 .submit()是一个非阻塞调用,返回Future;您正在运行任务时重置流。