Java:ファイルロックの方法(Windows)

以下はWindowsに限る。Linux、Macでは確認していない。

ロックの例

複数のプロセスでの排他制御のため、ロックファイルを利用する。例えば、以下のようなコードである。

public class Locker {

  static File lockFile = new File("test.lock");
  static FileChannel channel;
  static FileLock lock;

  public static void main(String[]args) throws Exception {
    lockFile.delete();

    channel = FileChannel.open(lockFile.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE);
    lock = channel.tryLock();
    System.out.println("locked? " + (lock != null));
    Thread.sleep(30 * 1000);
  }
}

冒頭の「lockFile.delete()」はわざと入れてある。

このプログラムは起動されると、30秒間ロックを保持し続け、プロセス終了と共にロックが解放される。

その間に、同じプログラムを起動しても、冒頭のlockFile.delete()で「java.nio.file.AccessDeniedException」になるし、「lockFile.delete()」を削除しても、ロックを取得することはできない。

ここまでは良いだろう。

ロックファイルが削除できてしまう。

しかし、どういう理由かわからないのだが、このtest.lockファイルはプログラムからは削除できないものの、エクスプローラからは削除できてしまうのである。一方でコマンドプロンプトで「del test.lock」としても削除できないようだ。

一体全体どういうことなのかわからないのだが、ともあれロックファイルが消されてしまう可能性があるということだ。

ロックの正しいコード

少なくともWindowsで正しく動作するコードとしては以下のようだ。単純にファイルチャネルをオープンしてロックをかけると上の問題は回避できない。

public class Locker {

  static File lockFile = new File("test.lock");
  static FileOutputStream outStream;
  static FileChannel channel;
  static FileLock lock;

  public static void main(String[]args) throws Exception {
    lockFile.delete();

    outStream = new FileOutputStream(lockFile);
    outStream.write(new byte[] { 0 });
    channel = outStream.getChannel();
    lock = channel.tryLock();     
    System.out.println("locked?" + (lock != null));

    Thread.sleep(30 * 1000);
  }
}

プロセスが終了すれば、ロックも解除されるが、ロックファイルはそのまま残る。
ロックファイルを削除したい場合は次のようにすればよい。

      Runtime.getRuntime().addShutdownHook(new Thread() { public void run() {
        try {
          lock.release();
        } catch (Exception ex) {}
        try {
          outStream.close();
        } catch (Exception ex) {}
        lockFile.delete();
      }});