在System.out上同步

时间:2014-06-02 14:14:06

标签: java file synchronization

我通过调用System.outSystem.setOutSystem.setErr更改为打印到文件。

每天午夜,我们想要重命名(存档)当前日志文件,并创建一个新文件。

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对象无效。

有谁知道如何确保在这里进行正确的同步?

4 个答案:

答案 0 :(得分:5)

我会在关闭旧的之前创建新的:

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

这样,旧的PrintStream在创建和分配新的PrintStream之前不会关闭。 System.out中始终存在有效的synchronized

不需要{{1}}阻止,因为所有内容都在同一个线程中。

答案 1 :(得分:3)

是的,您可以通过这种方式实现正确的同步。这是一个示例测试。

@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

答案 2 :(得分:3)

没有办法让这项工作安全,因为你有无法控制调用代码对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。考虑切换到那个。

编辑:实际上,实现此委派可以是 easy 。有一个构造函数PrintStream(OutputStream)。这意味着您可以在OutputStream中实现委托(其方法比PrintStream少得多)并将System.out设置为新的PrintStream(YourRetargettingOutputStream)。

答案 3 :(得分:1)

您可以明确定义一个对象,例如

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();
    }
}