![]()
FileLockで排他制御するサンプルPath path = Path.of(".lock");
try (FileChannel fc = FileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
// ロックの獲得を試みる
FileLock lock = fc.tryLock();
// ロックを獲得できなかったら処理を中止する
if (lock == null) {
return;
}
try {
// ここに同時実行させたくない処理を書く
} finally {
// ロック・ファイルを削除する
Files.deleteIfExists(path);
// ロックを解除する
lock.release();
}
}
1. FileLock を獲得していてもロック・ファイルは削除できる
FileChannel.tryLock() が成功して FileLock を獲得できても、 他のプロセスやエクスプローラーからロック ・ ファイルを削除できてしまいます。意図的にロック ・ ファイルを削除すると、 他プロセスが同じ名前のファイルを作成してロックを獲得できてしまうことに注意してください。
2. ロックを解除する前にロック・ファイルを削除する
lock.release() を呼び出してロックを解除した後にロック ・ ファイルを削除してはいけません。lock.release() を呼び出した直後に他のプロセスがロックを獲得している可能性があるためです。
ロック ・ ファイルを削除するなら lock.release() を呼び出す前にしましょう。もしくは、 ロック ・ ファイルを削除せずにそのまま残し続ける方法もあります。
3. DELETE_ON_CLOSE を指定してはいけない
FileChannel.open に DELETE_ON_CLOSE を指定してはいけません。これを指定していると、 他プロセスが FileChannel.open を呼び出したときに java.nio.file.AccessDeniedException がスローされます。
そもそも、 DELETE_ON_CLOSE や File.deleteOnExit() ではロック ・ ファイルを削除するタイミングが遅いです。これらは JavaVM の終了時にファイルを削除します。lock.release 呼び出しによってロックが解除された後に (他プロセスがロックを獲得できる状態になってから)、 ロックを保持していないプロセスがロック ・ ファイルを削除することになります。
4. FileChannel.tryLock() は再入可能ではない
FileChannel.tryLock() を呼び出してロックを獲得した後、 もう一度、 同じ JavaVM プロセス内で、 同じファイル ・ チャネルに対して FileChannel.tryLock() を呼び出すと java.nio.channels.OverlappingFileLockException がスローされます。
FileChannel.tryLock() は ReentrantLock などとは異なり再入可能ではないことに注意してください。