我正在用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();
}
}
}
答案 0 :(得分:4)
每次运行时都会删除锁定文件,因此只有一个进程可以锁定它。
当您使用FileLock
时,它纯粹是建议 - 获取文件上的锁定可能无法阻止您做任何事情......读取,写入和删除文件都是可能的即使另一个进程已获得锁定。有时,锁定在特定平台上可能会比这更多,但是这种行为是未指定的,并且依赖于类文档中保证的更多是失败的一种方法。
“顾问锁定”仅一个信号,该信号对其他需要查找的进程可见。如果您不仅仅依赖它,那么当您在其他平台上运行时,您的程序就会中断。
为什么要删除锁定文件呢?锁定文件就像一个布尔标志,对系统上的每个进程都是可见的。设计您的协议以便以这种方式使用它,并且您将拥有可靠的跨平台锁定机制。
答案 1 :(得分:2)
为什么不将PID保存到文件中,而不是锁定文件,验证是否存在具有该ID的进程。如果有,并且它是您的应用程序的实例,您知道它已经在运行。
套接字也可能是一个好主意,因为您可以使用它与正在运行的实例进行通信。
修改强>
此外,来自FileLock's javadoc:
锁是否实际阻止其他程序访问 锁定区域的内容是系统相关的 未指定的。
答案 2 :(得分:1)
我使用了相同的样本并且在Mac OS X上遇到了同样的问题。似乎文件锁定不会阻止POSIX系统上的文件删除。在你解锁之前,你的应用程序仍然会对该文件有某种处理方式。因此,请考虑在其名称(或文件内部)中使用带有PID的锁定文件。
答案 3 :(得分:1)
使用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();
答案 4 :(得分:0)
我在Windows和Linux上测试过它。工作良好。当应用程序正常关闭时,锁定文件会自动删除。因此,当您重新启动应用程序时,您不必担心锁定文件停留在那里。只需注释掉以下几行:
if (f.exists()) {
// if exist try to delete it
f.delete();
}
但是,您可能需要考虑如果您的应用程序崩溃并且没有以正常方式关闭会发生什么。
答案 5 :(得分:0)
最近我遇到了同样的问题,但在我的情况下我有一个优势:我的应用程序仅在一些超时后轮询某个目录。由于我的应用程序没有立即轮询目录,我编写了特殊的类,在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
}
}