带/不带 TRUNCATE_EXISTING 的 StandardOpenOption.WRITE + StandardOpenOption.CREATE 之间的区别?

gur*_*ing 1 java nio file java-io

可能是一个简单的答案,但我尝试阅读有关 StandardOpenOption 的 javadoc 文档,但我仍然不清楚当我说时会发生什么

Files.write(..., ..., StandardOpenOption.WRITE, StandardOpenOption.CREATE); // Write A

根据一些本地测试,看起来如果文件已经存在,这仍然会覆盖该文件,那么上面和上面有什么区别

Files.write(..., ..., StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); // Write B

此外,我有一个带有两个线程的程序,可以从写入的文件中读取/写入。当我使用 Write B 时,我有时会遇到竞争条件,其中一个线程创建了该文件,而另一个线程在第一个线程正在读取时覆盖该文件,并且出现异常。但是当我使用 Write A 时,我永远不会遇到这种竞争条件。几乎就像它会先锁定文件一样?有人可以解释幕后发生了什么吗?

Mar*_*idi 5

当您使用时,StandardOpenOption.TRUNCATE_EXISTING如果文件在任何写入之前已经存在,则文件的长度首先被“截断为 0”:

$ jshell

jshell> import java.nio.file.*;

jshell> Path path = Paths.get("foo");
path ==> foo

jshell> Files.write(path, "AAAA".getBytes(), StandardOpenOption.WRITE, StandardOpenOption.CREATE)
Run Code Online (Sandbox Code Playgroud)

我刚刚创建了一个名为“foo”的新文件,其中包含字符串“AAAA”:

$ more foo
AAAA
$
Run Code Online (Sandbox Code Playgroud)

现在我将字符串“BB”写入文件,不带 StandardOpenOption.TRUNCATE_EXISTING选项:

jshell> Files.write(path, "BB".getBytes(), StandardOpenOption.WRITE, StandardOpenOption.CREATE)
Run Code Online (Sandbox Code Playgroud)

只有前两个字符被覆盖,其他字符仍然存在:

$ more foo
BBAA
$
Run Code Online (Sandbox Code Playgroud)

现在我再次写入字符串“BB”,但我添加了 StandardOpenOption.TRUNCATE_EXISTING选项:

jshell> Files.write(path, "BB".getBytes(), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)
Run Code Online (Sandbox Code Playgroud)

StandardOpenOption.TRUNCATE_EXISTING该文件仅包含“BB”,因为其先前的内容在写入之前已被“擦除” :

$ more foo
BB
$
Run Code Online (Sandbox Code Playgroud)