簡體   English   中英

在System.out上同步

[英]Synchronizing on System.out

我通過調用System.setOutSystem.setErrSystem.out更改為打印到文件。

每天午夜,我們都想重命名(存檔)當前日志文件,並創建一個新文件。

if (out != null) {
    out.close();
    out = null;
    File f = new File(outputfilename);
    f.renameTo(new File(dir.getPath().replace(".log", "-" + System.currentTimeMillis() + ".log")))
    StartLogFile();
}

StartLogFile()

if (out == null) {
    out = new FileOutputStream(outputfilename, true);
    System.setOut(new PrintStream(out));
    System.setErr(new PrintStream(out));
}

我已經把異常處理了。

我擔心的是,如果某些東西試圖在out.close()setOut / setErr之間打印,我將錯過一個日志。

我真正的問題是,如何通過對System.out.println其他調用來創建這個原子? 我正在考慮嘗試

synchronized (System.out) {

}

但我真的不確定這里的內在鎖是否有任何作用。 特別是因為我在操作期間使out對象無效。

有誰知道我怎么能確保在這里正確同步?

在關閉舊的之前我會創建新的:

PrintStream old = System.out;
out = new FileOutputStream(outputfilename, true);
System.setOut(new PrintStream(out));
old.close();

這樣,在創建和分配新的PrintStream之前,不會關閉舊的PrintStream 在System.out中始終有一個有效的PrintStream

不需要synchronized塊,因為所有內容都在同一個線程中。

是的,你可以用這種方式實現正確的同步 這是一個示例測試。

@Test
public void test() throws InterruptedException {

    new Thread(()->{
        while(true){
            System.out.println("printing something");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }).start();

    Thread.sleep(500);
    synchronized (System.out){
        System.out.println("changin system out");
        Thread.sleep(2000);
        System.out.println("finished with sysout");
    }
    Thread.sleep(2000);
}

輸出將是:

printing something
printing something
printing something
printing something
printing something
changin system out
finished with sysout
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something

沒有辦法使這項工作安全 ,因為你無法控制調用代碼對System.out的作用。 想一想:

public void doSomethingTakingALongTime(PrintStream target) {
    // lots of code
}

// somewhere else
doSomethingTakingALongTime(System.out);

永遠不能確定在局部變量或方法參數中的某處沒有System.out引用的副本。

解決這個問題的正確方法是在程序的最開始只設置一次 System.out,而不是使用標准的PrintStream,而是使用自己的實現將所有內容委托給當前目標。

然后,您可以完全控制通過System.out進行的每個輸出,並可以在需要時隨意進行同步。 如果您自己的實現同步每個操作,那么在您更改日志記錄目標時會發生什么的問題甚至不會出現 - 每個其他調用者都會被同步鎖阻塞。

順便說一句。 使用System.out進行日志記錄是值得懷疑的。 記錄的事實標准將使用log4j。 考慮切換到那個。

編輯:實際上實現這個委托可以相當容易 有一個構造函數PrintStream(OutputStream)。 這意味着您可以在OutputStream中實現委托(其方法比PrintStream少得多)並將System.out設置為新的PrintStream(YourRetargettingOutputStream)。

您可以顯式定義對象以進行鎖定

static final Object lock = new Object();

如下鎖定它怎么樣

synchronized(lock){
    if(out != null) {
        out.close();
        out = null;
        File f = new File(outputfilename);
        f.renameTo(new File(dir.getPath().replace(".log", "-" + System.currentTimeMillis() + ".log")))
        StartLogFile();
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM