这些方法如何允许/导致数据在磁盘上丢失?

How are these methods allowing / causing data to be lost on disk?

我有一个程序会每隔一段时间(大约 15 秒)将其设置和数据写入磁盘。

如果程序是运行并且计算机突然关闭——例如,在墙上断电——不知何故我磁盘上的所有数据文件都变成了空文件。

这是我的代码,我认为我设计的代码是为了防止这种故障,但根据测试故障仍然存在:

SaveAllData -- 每隔一段时间调用一次,在调用 JavaFX.Application.stop() 时也会调用。

public void saveAllData () {
    createNecessaryFolders();
    saveAlbumsAndTracks();
    saveSources();
    saveCurrentList();
    saveQueue();
    saveHistory();
    saveLibraryPlaylists();
    saveSettings();
    saveHotkeys();
}

CreateNecessaryFolders

private void createNecessaryFolders () {
    if ( !playlistsDirectory.exists() ) {
        boolean playlistDir = playlistsDirectory.mkdirs();
    }
}

保存函数 -- 它们看起来都像这样

public void saveCurrentList () {
    File tempCurrentFile = new File ( currentFile.toString() + ".temp" );
    try ( ObjectOutputStream currentListOut = new ObjectOutputStream( new FileOutputStream( tempCurrentFile ) ) ) {
        currentListOut.writeObject( player.getCurrentList().getState() );
        currentListOut.flush();
        currentListOut.close();

        Files.move( tempCurrentFile.toPath(), currentFile.toPath(), StandardCopyOption.REPLACE_EXISTING );

    } catch ( Exception e ) {
        LOGGER.warning( e.getClass().getCanonicalName() + ": Unable to save current list to disk, continuing." );
    }
}

Github repository to commit where this problem exists. See Persister.java.

正如我所说,当突然断电时所有通过这种方法保存的设置文件都会被清空。这对我来说特别没有意义,因为它们是按顺序调用的,并且我确保在调用 move() 之前将文件写入磁盘并刷新。

知道这是怎么发生的吗?我想通过调用 flush、close,然后 move,我可以确保在覆盖旧数据之前将数据写入磁盘。不知何故,情况并非如此,但我一无所知。有什么建议么?

注意:这些文件由这些函数写入,由相应的load()函数读取。在我的程序中的任何其他地方都无法访问这些文件。

注意 2:我在 Ubuntu Linux 16.10 遇到了这个问题。我还没有在其他平台上测试过它。

StandardCopyOption.ATOMIC_MOVE添加到Files.move()调用解决了问题:

public void saveCurrentList () {
    File tempCurrentFile = new File ( currentFile.toString() + ".temp" );
    try ( ObjectOutputStream currentListOut = new ObjectOutputStream( new FileOutputStream( tempCurrentFile ) ) ) {
        currentListOut.writeObject( player.getCurrentList().getState() );
        currentListOut.flush();
        currentListOut.close();

        Files.move( tempCurrentFile.toPath(), currentFile.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE );

    } catch ( Exception e ) {
        LOGGER.warning( e.getClass().getCanonicalName() + ": Unable to save current list to disk, continuing." );
    }
}