【问题标题】:Unable to lock files on Linux using java.nio.channels.FileLock无法使用 java.nio.channels.FileLock 在 Linux 上锁定文件
【发布时间】:2012-06-26 06:38:49
【问题描述】:

我正在用 Java 创建一个应用程序,我只希望运行一个实例。为此,我创建了一个文件并在我的应用程序运行时获得了锁。

我有以下代码在 Windows 上工作,但在 Linux 上失败:一旦我获得了一个锁而不解锁它,我可以获得另一个锁。

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

public class MyApp {

private static File f;
private static FileChannel channel;
private static FileLock lock;

public static void main(String[] args) {
    try {
        f = new File("RingOnRequest.lock");
        // Check if the lock exist
        if (f.exists()) {
            // if exist try to delete it
            f.delete();
        }
        // Try to get the lock
        channel = new RandomAccessFile(f, "rw").getChannel();
        lock = channel.tryLock();
        if(lock == null)
        {
            // File is lock by other application
            channel.close();
            throw new RuntimeException("Only 1 instance of MyApp can run.");
        }
        // Add shutdown hook to release lock when application shutdown
        ShutdownHook shutdownHook = new ShutdownHook();
        Runtime.getRuntime().addShutdownHook(shutdownHook);

        //Your application tasks here..
        System.out.println("Running");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
    catch(IOException e)
    {
        throw new RuntimeException("Could not start process.", e);
    }

}

public static void unlockFile() {
    // release and delete file lock
    try {
        if(lock != null) {
            lock.release();
            channel.close();
            f.delete();
        }
    } catch(IOException e) {
        e.printStackTrace();
    }
}

static class ShutdownHook extends Thread {

    public void run() {
        unlockFile();
    }
}

}

【问题讨论】:

  • 一旦我获得了一个锁而不解锁它,我可以获得另一个锁。
  • 那是因为您正在删除文件。不要删除文件。当您为每个程序实例创建一个新文件时,您不能希望获得一个文件的排他锁。

标签: java linux filelock


【解决方案1】:

您每次运行时都会删除锁定文件,因此只有一个进程可以对其进行锁定。

当您使用FileLock 时,它纯粹是建议——获取文件锁定可能不会阻止您做任何事情……即使在另一个进程获得了锁。有时,在特定平台上,锁可能会做更多的事情,但这种行为是未指定的,并且依赖于类文档中所保证的更多内容是失败的秘诀。

“通知锁”只是一个信号,其他进程可以看到它,而其他进程会费心寻找它。如果您对它的依赖不止于此,您的程序在其他平台上运行时会中断。

你为什么要删除锁定文件?锁定文件就像一个布尔标志,对系统上的每个进程都是可见的。将您的协议设计为以这种方式使用它,您将拥有一个可靠的跨平台锁定机制。

【讨论】:

    【解决方案2】:

    为什么不将 PID 保存到文件中,而不是锁定文件,而是验证是否存在具有该 ID 的进程。如果有,并且它是您的应用程序的一个实例,您就知道它已经在运行了。

    套接字也可能是个好主意,因为您可以使用它与正在运行的实例进行通信。

    编辑:

    另外,来自FileLock's javadoc

    锁是否真正阻止其他程序访问 锁定区域的内容是系统相关的,因此 未指定。

    【讨论】:

    • 我如何以编程方式做到这一点?
    • 我在这里得到了解决方案advancedinstaller.com/user-guide/…
    • 如果你运行那个“advanceinstaller”,这实际上是真的,而不是在任何普通的java应用程序中。 - 互联网上没有其他关于该 secondaryMain 的引用,我对其进行了测试,并且每次都会调用 main()。
    • 通过在 java.io.tmpdir 目录中创建该文件来使锁工作。
    • -1: pidfiles 的创建和修改不是原子的,验证它们指向的过程也不是原子的(必须在读取后异步完成)。这使得它们对于实际锁定是不安全的。
    【解决方案3】:

    使用mkdir。在 unix 系统上,这是一个atomic operation——如果成功创建了新目录,它将成功,否则将失败。

    例子:

    File lockFile = new File("/path/to/lockdir");
    boolean hasLock = lockFile.mkdir();
    if (!hasLock) {
      throw new IOException("could not get lock");
    }
    // do stuff
    lockFile.delete();
    

    【讨论】:

    • 如果应用程序因任何原因意外停止运行而未删除 lockFile 文件夹怎么办? .. 下次运行应用程序时它不会运行,因为该文件夹存在!
    【解决方案4】:

    我使用了与您相同的示例,并在 Mac OS X 上遇到了同样的问题。似乎文件锁定并不能阻止 POSIX 系统上的文件删除。在您解锁之前,您的应用程序仍会对该文件具有某种句柄。因此,请考虑使用名称(或文件内部)中带有 PID 的锁定文件。

    【讨论】:

    • 在文件名中添加“PID”会有什么不同?
    • 这是另一种实现锁文件的方式。您的程序会在其名称 (myprogram.PID) 或其内部创建一个带有 PID 的文件。下一个启动的实例将检查与使用的模式匹配的文件,提取 PID 并检查是否存在具有该 PID 的进程。如果进程存在 - 当前实例是第二个并且必须关闭,否则 - 它只是一个实例,它会创建一个新的 PID 文件并继续运行。
    • -1:pidfile 不提供真正的锁定,因为它们的创建、修改、读取等。不是原子的,因此容易出现竞争条件。因此,它们需要另一种锁定机制结合使用以确保它们的安全。
    【解决方案5】:

    我在 Windows 和 Linux 上都对其进行了测试。工作正常。当应用程序正常关闭时,锁定文件会自动删除。因此,当您重新启动应用程序时,您不必担心锁定文件会留在那里。只需注释掉以下几行:

    if (f.exists()) {
        // if exist try to delete it
        f.delete();
    }
    

    但是,您可能需要考虑如果您的应用程序崩溃并且没有以正常方式关闭会发生什么。

    【讨论】:

      【解决方案6】:

      最近我遇到了同样的问题,但就我而言,我有一个优势:我的应用程序仅在超时后才轮询某个目录。由于我的应用程序没有立即轮询目录,因此我编写了特殊的类,该类在 init 方法中使用他自己的 PID 创建锁定文件,之后在它尝试使用目录之前它需要调用 ownedLock() - 如果它返回 true 那么我们可以否则退出(代码在 Kotlin 中,但你会明白主要思想):

      import java.io.File
      import java.lang.management.ManagementFactory
      class DirectoryLocker(private val directory: String, private val lockName: String) {
         private val lockFile by lazy { File("$directory/$lockName.lock") }
      
         // Will try to acquire lock to directory, whoever last writes its pid to file owns the directory
         fun acquireLock() = with(lockFile) {
            createNewFile()
            writeText(currentPID())
         }
      
         fun ownedLock(): Boolean = lockFilePid() == currentPID()
      
         fun releaseOwnedLock() {
            if(lockFilePid() == currentPID()) lockFile.delete()
         }
      
         private fun currentPID(): String {
            val processName = ManagementFactory.getRuntimeMXBean().name
            return processName.split("@".toRegex()).first()
         }
      
         private fun lockFilePid(): String? {
            return if(lockFile.exists()) lockFile.readLines().first() else null
         }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多