如何正确关闭OutputStream?

时间:2015-02-25 09:37:29

标签: java file outputstream

下面你会找到服务器的构造函数来设置它。简而言之,它从上次读取List个对象(称为log)和两个int(称为currentTerm和{ {1}})。任何时候任何这些“易变”字段都会被更新,相关文件也必须更新(因此要更新一致状态),所以我需要两个votedFor调用ObjectOutPutStreammetadataWriter。由于服务器可能随时 ,我无法编写任何logWriter方法。您认为下次我设置服务器时(在读取操作期间)避免close()的唯一可能解决方案是每次EOFException输出流(就像我已经完成的那样)代码的最后几行)?

flush()

重要提示:

我不认为使用它们的每个方法中的try-with-resources (以及更新服务器状态的每个方法)都是一个可行的解决方案,因为它会好的 public ServerRMI(...) { ... log = new ArrayList<>(); try { //if Log file exists then should exists Metadata too if(Files.exists(Paths.get("Server" + id + "Log"), LinkOption.NOFOLLOW_LINKS)) { reader = new ObjectInputStream(new FileInputStream("Server"+id+"Log")); try { while(true) log.add((LogEntry) reader.readObject()); } catch (EOFException e){} catch (ClassNotFoundException e) { e.printStackTrace(); } reader = new ObjectInputStream(new FileInputStream("Server"+id+"Metadata")); currentTerm = reader.readInt(); votedFor = reader.readInt(); } else//if it is the first time that the server is set up initialize all persistent fields { currentTerm = 1; votedFor = -1; log = new ArrayList<LogEntry>(); } logWriter = new ObjectOutputStream(new FileOutputStream("Server"+id+"Log")); metadataWriter = new ObjectOutputStream(new FileOutputStream("Server" + id + "Metadata")); //since creating a new ObjectOutputStream overwrite the old file with the an empty one, as first thing we rewrite the old content for(LogEntry entry : log) logWriter.writeObject(entry); metadataWriter.writeInt(currentTerm); metadataWriter.writeInt(votedFor); metadataWriter.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } ... } 文件(写在其上的两个metadata总是在每次更新/写入文件时被替换)但是对于int文件,它意味着每个都写入整个列表它被改变的时间(在它上面进行的操作不仅是appen,而且也是替换!),我认为它对性能不太好!

2 个答案:

答案 0 :(得分:2)

我建议您使用try-with-resource并立即编写整个集合,并且在获得异常之前不必阅读。您可以在开始时编写元数据。

List<LogEntry> entries = ..
try (ObjectOutputStream out = new ObjectOutputStream(....)) {
    out.writeObject(currentTerm);
    out.writeObject(votedeFor);
    out.writeObject(entries);
}

阅读你可以做到

String currentTerm = "none";
String votedFor = "no-one";
List<LogEntry> entries = Collections.emptyList();
try (ObjectInputStream in = new ObjectInputStream(....)) {
    currentTerm = (String) in.readObject();
    votedFor = (String) in.readObject();
    entries = (List<LogEntry>) in.readObject();
}

注意:这需要Java 7,但鉴于它即将成为EOL,如果可以,我建议升级到Java 8.

  

我认为它对表现不太好!

这是一个非常不同的问题,但你仍然会使用try-with-resource。解决方案是仅添加新条目。要做到这一点,你需要,

  • 在ObjectOutputStream之上的协议,它支持附加到文件。 ObjectOutputStream不会为你做这件事,而是你需要编写数据块,你需要一种方法将它们粘在一起。
  • 一种确定自上次写入磁盘后已更改/添加的内容并仅将更改写入磁盘的方法。

Befor eyou假设性能不好,你应该测试它,因为你可能会发现不值得增加复杂性来节省几毫秒。

顺便说一句,如果你真的关心性能,不要使用ObjectOutputStream,它通用且灵活,但速度很慢。

  

所以OutpuStreamObject(它是类字段,而不是构造函数局部变量)未正确关闭。

在这种情况下,文件将被破坏。您需要一种验证文件并截断​​无效数据的方法。


看一下我写的图书馆。 Chronicle Queue它可以解决您支持的许多问题

  • 从多个线程,进程或机器连续写入。
  • 如果进程或线程在写入时死亡,则会截断或忽略损坏的条目。
  • 如果进程在写入之间死亡,则不会丢失数据。
  • 它支持更有效的序列化方法。
  • 当它以微秒延迟写入时,它可被多个线程/进程读取。
  • 可以将其复制到多台计算机上进行分发。
  • 条目可以通过索引或二进制搜索随机访问。

答案 1 :(得分:1)

The try-with-resources Statement

  

try-with-resources语句是一个声明一个或多个资源的try语句。资源是在程序完成后必须关闭的对象。 try-with-resources语句确保在语句结束时关闭每个资源。

实施例

static String readFirstLineFromFile(String path) throws IOException {
    try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}