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