2009-02-27 49 views
30

我有一个机器集群,每个运行一个Java应用程序。如何在Java中自动重命名文件,即使dest文件已经存在?

这些Java应用程序需要一致地访问唯一的resource.txt文件。

我需要在Java中原子地将temp.txt文件重命名为resource.txt,即使resource.txt已经存在。

删除resource.txt并重命名temp.txt不起作用,因为它不是原子的(它创建了一个很小的时间段,其中resource.txt不存在)。

它应该是跨平台的...

谢谢!

+0

自我提醒,8年后:我不记得的背景下,但如果* *问题,需要跨平台的原子性,也许*解决方案*不应该涉及一个文件? – 2018-02-12 09:32:22

回答

12

在Linux上(我相信Solaris和其他UNIX操作系统),如果Java的File.renameTo()方法存在,它将覆盖目标文件,但在Windows下不是这种情况。

为了跨平台,我想你必须在resource.txt上使用文件锁定,然后覆盖数据。

该文件锁的行为是 平台相关。在某些平台上, 文件锁定为通知,这意味着 ,除非应用程序检查文件锁定 ,否则不会阻止 访问该文件。在其他 平台上,文件锁定是强制性的, 这意味着文件锁定会阻止 任何应用程序访问 文件。

try { 
    // Get a file channel for the file 
    File file = new File("filename"); 
    FileChannel channel = new RandomAccessFile(file, "rw").getChannel(); 

    // Use the file channel to create a lock on the file. 
    // This method blocks until it can retrieve the lock. 
    FileLock lock = channel.lock(); 

    // Try acquiring the lock without blocking. This method returns 
    // null or throws an exception if the file is already locked. 
    try { 
     lock = channel.tryLock(); 
    } catch (OverlappingFileLockException e) { 
     // File is already locked in this thread or virtual machine 
    } 

    // Release the lock 
    lock.release(); 

    // Close the file 
    channel.close(); 
} catch (Exception e) { 
} 

的Linux,默认情况下,采用自愿锁定,而Windows强制执行它。也许你可以检测到操作系统,并在Windows下使用一些锁定代码在UNIX下使用renameTo()?

还有一种方法可以在Linux下针对特定文件启用强制锁定,但它有点模糊。您必须正确设置模式位。

Linux的,下面的System V(请参阅系统 V接口定义(SVID)版本 3),让SGID位的文件 没有组执行权限标志 文件进行强制性锁定

+0

这应该被标记为不推荐使用,因为新方法应该是java.nio! – user1052080 2013-09-24 13:33:18

+0

只有当文件非常小以至于可以在一次写入操作中被覆盖时,这才有效。 – 2014-07-21 09:17:51

+0

@ user1052080不幸的是,NIO只能解决这个问题。 NIO不支持除从流写入之外的文件操作同步。这有时是一个关键问题。 – Leliel 2016-06-02 03:24:30

1

如果这应该是跨平台的,我建议2种选择:

  1. 实现负责所有文件访问的中间服务。在这里,您可以使用多种机制来同步请求。每个客户端Java应用程序只通过此服务访问文件。
  2. 每次需要执行同步操作时,创建一个控件文件。访问该文件的每个Java应用程序都负责检查控件文件并等待此控件文件存在。 (几乎就像一个信号量)。执行删除/重命名操作的过程负责创建/删除控件文件。
2

如所述here,它看起来像Windows操作系统甚至不支持旧版本的原子文件重命名。很可能你必须使用一些手动锁定机制或某种交易。为此,您可能需要查看apache commons transaction包。

1

如果更名的目的是为了取代上飞和resource.txt你在所涉及的所有程序的控制,更换频率不高,你可以做到以下几点。

要打开/读取文件:

  1. 打开 “resource.txt”,如果失败
  2. 打开 “resource.old.txt”,如果失败
  3. 打开“resource.txt “再一次,如果失败
  4. 你有一个错误条件。

要替换的文件:

  1. 重命名 “resource.txt” 到 “resource.old.txt”,然后
  2. 重命名 “resource.new.txt” 到 “resource.txt” ,然后
  3. 删除“resource.old.txt”。

这将确保您的所有读者总能找到有效的文件。

但是,更方便,是简单地尝试在一个循环的开放,如:

InputStream inp=null; 
StopWatch tmr=new StopWatch();      // made up class, not std Java 
IOException err=null; 

while(inp==null && tmr.elapsed()<5000) {    // or some approp. length of time 
    try { inp=new FileInputStream("resource.txt"); } 
    catch(IOException thr) { err=thr; sleep(100); } // or some approp. length of time 
    } 

if(inp==null) { 
    // handle error here - file did not turn up after required elapsed time 
    throw new IOException("Could not obtain data from resource.txt file"); 
    } 

... carry on 
1

你可能通过重命名它(和删除你的文件之前建立的文件filechannel锁获得了一些牵引力一旦你有锁,你将重写)。 -r

30

对于Java 1.7+,使用java.nio.file.Files.move(Path source, Path target, CopyOption... options)和CopyOptions“REPLACE_EXISTING”和“ATOMIC_MOVE”。

See API documentation for more information.

例如:

Files.move(src, dst, StandardCopyOption.ATOMIC_MOVE); 
相关问题