[英]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.