For some reason this solution to prevent multiple instances of the app from running doesn't work anymore - what's wrong?
I expect the second launch of this app will print: "another instance is running" and will stop with the error, but it doesn't happen. The second instance works the same as the first.
import java.io.File;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.StandardOpenOption;
public class JustOneTest {
public static void main(String[] args) {
runCheck();
while (true) {
try {
System.out.print(".");
Thread.sleep(5 * 60);
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static void runCheck() {
System.out.println("start check: is app running");
final File file = new File(System.getProperty("user.home"), "khf.lock");
try {
FileLock lock;
try (FileChannel fc = FileChannel.open(file.toPath(),
StandardOpenOption.CREATE,
StandardOpenOption.WRITE)) {
lock = fc.tryLock();
}
if (lock == null) {
System.out.println("another instance is running");
throw new Error("another instance is running");
}
System.out.println(lock);
// output: sun.nio.ch.FileLockImpl[0:9223372036854775807 exclusive invalid]
} catch (IOException e) {
throw new Error(e);
}
System.out.println("start check completed");
}
}
Your created FileLock
instance get invalidated after your try
block because you close the FileChannel
, which was used to get the FileLock
instance. This is specified in the java.nio.channels.FileLock documentation:
A file-lock object is initially valid. It remains valid until the lock is released by invoking the
release
method, by closing the channel that was used to acquire it, or by the termination of the Java virtual machine, whichever comes first.
(bold emphasis mine)
So when you close the FileChannel
in your try
block, you also close the FileLock
instance, which releases the lock.
To prevent releasing the lock after you have acquired it, you have to keep the FileChannel
instance "alive" and not close it (same for the FileLock
instance). This means you might want to put the FileChannel
instance in a static field and "keep it there". See the following adjustment in your code:
import java.io.File;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.StandardOpenOption;
public class JustOneTest {
public static FileLock lock;
public static FileChannel fc;
public static void main(String[] args) {
runCheck();
while (true) {
try {
System.out.print(".");
Thread.sleep(5 * 60);
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static void runCheck() {
System.out.println("start check: is app running");
final File file = new File(System.getProperty("user.home"), "khf.lock");
try {
fc = FileChannel.open(file.toPath(),
StandardOpenOption.CREATE,
StandardOpenOption.WRITE);
lock = fc.tryLock();
System.out.println(lock);
if (lock == null) {
System.out.println("another instance is running");
throw new Error("another instance is running");
}
} catch (IOException e) {
throw new Error(e);
}
System.out.println("start check completed");
System.out.println(lock);
System.out.println(lock.isValid());
}
}
This fill generate the following example output in the first java execution instance:
start check: is app running
sun.nio.ch.FileLockImpl[0:9223372036854775807 exclusive valid]
start check completed
sun.nio.ch.FileLockImpl[0:9223372036854775807 exclusive valid]
true
.........
This is the output of the second java execution instance, while the first one is running:
start check: is app running
null
another instance is running
Exception in thread "main" java.lang.Error: another instance is running
at JustOneTest.runCheck(JustOneTest.java:38)
at JustOneTest.main(JustOneTest.java:12)