【发布时间】:2014-01-15 13:34:28
【问题描述】:
所以我有一个使用固定线程池来执行一些代码的 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 问题:In a multithreaded Java program, does each thread have its own copy of 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 that blocks on submission after a certain queue size 的答案中概述的小型阻塞队列。我基本上陷入了认为newFixedThreadPool() 中的默认设置适用于我的情况的陷阱,而实际上它们并没有,而布赖恩的回答有助于揭露这些错误的假设。谢谢布赖恩!
【问题讨论】:
-
我怀疑问题在于
System流上的final关键字的含义与在整个Java 其余部分中的含义不同,并且它有自己的“写保护”语义。您可能会收到有关启动线程池的指令重新排序。尝试在调用setErr和初始化线程池之间放入一个无用的volatile Object类变量并调用它的hashCode()方法。 -
@chrylis 实际上,
System中的final与 java 的其余部分具有相同的语义:stackoverflow.com/a/3301720/2299084。 -
听起来你需要在每个线程中重定向
System.err,并使/* custom logging stream here */线程安全。 -
@chrylis 谢谢,我正在尝试那个“技巧”,但由于某种原因,当我尝试使用它时,我的程序会立即退出。没有消息,退出代码 1,也许消息被吞了,因为我重定向了 System.err :)。可惜我得走了,所以我明天再试试。
-
@user60561 No, it doesn't.
标签: java multithreading io outputstream