繁体   English   中英

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

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

因此,我有一个使用固定线程池的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配置完全删除输出。 完善。

当我开始添加多线程时,我做了一些研究,并遇到了一个SO问题: 在多线程Java程序中,每个线程是否都有其自己的System.out副本? ,这意味着我应该在启动线程池之前执行一次System.setErr(),然后一切准备就绪。 只是事实并非如此。 我的代码现在看起来像这样:

 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,好像我根本没有进行调用一样。 讨厌鬼!

我还尝试在执行任务时让线程调用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)

谢谢。

编辑:我的问题基本上可以通过接受的答案解决,但是出于完整性考虑,我想补充一点,我无法使用Futures和get()解决我的特殊情况。 我的单个任务占用大量RAM,但持续时间各不相同,因此我不得不使用一个小的阻塞队列,如Java答案:ExecutorService所述,该阻塞队列在一定队列大小后提交时会阻塞 我基本上陷入了这样一种陷阱:认为newFixedThreadPool()中的默认值在我的情况下不起作用,而实际上却不行,而Brian的回答有助于揭露那些错误的假设。 谢谢布莱恩!

以下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行,每行显示“ This is to err stream”,最后,在控制台上又打印了一行。

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

如果不是这样,它将解释您在注释中所做的声明,即“我将看到输出的片段打印到System.err,而片段打印到我的日志文件” ……这几乎是您期望发生的事情。 .submit()是一个非阻塞调用,它返回一个Future 您正在运行任务时重置流。

暂无
暂无

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

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