web-dev-qa-db-ja.com

Java読み取りおよび書き込み用のFileLock

特定の移動関連コマンドが含まれているファイルを読み取るために、cronからかなり頻繁に呼び出されるプロセスがあります。私のプロセスは、このデータファイルの読み取りと書き込みを行う必要があります。また、この間、他のプロセスがこのデータファイルにアクセスしないように、ロックを維持する必要があります。完全に別個のプロセスをユーザーが実行して、この同じデータファイルに(潜在的に)書き込み/追加することができます。これらの2つのプロセスでNiceを再生し、一度に1つずつファイルにアクセスするようにします。

Nio FileLockは私が必要としていたもののようでした(私自身のセマフォタイプのファイルを書くことはできませんでした)が、読み取りのためにロックするのに問題があります。ロックと書き込みは問題なく実行できますが、読み取り時にロックを作成しようとすると、NonWritableChannelExceptionが発生します。読み取り用にファイルをロックすることも可能ですか? RandomAccessFileは私が必要としているものに近いようですが、それを実装する方法がわかりません。

失敗するコードは次のとおりです。

FileInputStream fin = new FileInputStream(f);
FileLock fl = fin.getChannel().tryLock();
if(fl != null) 
{
  System.out.println("Locked File");
  BufferedReader in = new BufferedReader(new InputStreamReader(fin));
  System.out.println(in.readLine());
          ...

例外はFileLock行でスローされます。

Java.nio.channels.NonWritableChannelException
 at Sun.nio.ch.FileChannelImpl.tryLock(Unknown Source)
 at Java.nio.channels.FileChannel.tryLock(Unknown Source)
 at Mover.run(Mover.Java:74)
 at Java.lang.Thread.run(Unknown Source)

JavaDocsを見ると、

最初に書き込み用に開かれていないチャネルに書き込もうとすると、チェックされていない例外がスローされます。

しかし、私は必ずしもそれに書く必要はありません。書き込み目的でFileOutpuStreamなどを作成しようとすると、同じファイルでFileInputStreamを開こうとするまでは問題ありません。

22
bobtheowl2

(a)ファイルをロックしても、他のプロセスもロックを使用しない限り、ファイルにアクセスできなくなることをご存知ですか?
(b)書き込み可能なチャネルを介してロックする必要があります。 「rw」モードでRandomAccessFileを介してロックを取得し、FileInputStreamを開きます。必ず両方を閉じてください!

18
user207421

tryLock(0L, Long.MAX_VALUE, true)を使用してロックを作成した方がよいでしょう。

これにより、読み取りに適切な共有ロックが作成されます。

tryLock()tryLock(0L, Long.MAX_VALUE, false)の省略形です。つまり、排他的な書き込みロックを要求します。

13
Huxi

ファイルロックの有効性を確認するために、テストプログラムとbashコマンドを作成しました。

import Java.io.File;
import Java.io.RandomAccessFile;
import Java.nio.channels.FileLock;

public class FileWriterTest
{
    public static void main(String[] args) throws Exception
    {
        if (args.length != 4)
        {
            System.out.println("Usage: FileWriterTest <filename> <string> <sleep ms> <enable lock>");
            System.exit(1);
        }

        String filename = args[0];
        String data = args[1];
        int sleep = Integer.parseInt(args[2]);
        boolean enableLock = Boolean.parseBoolean(args[3]);

        try (RandomAccessFile raFile = new RandomAccessFile(new File(filename), "rw"))
        {
            FileLock lock = null;
            if (enableLock)
            {
                lock = raFile.getChannel().lock();
            }

            Thread.sleep(sleep);
            raFile.seek(raFile.length());
            System.out.println("writing " + data + " in a new line; current pointer = " + raFile.getFilePointer());
            raFile.write((data+"\n").getBytes());

            if (lock != null)
            {
                lock.release();
            }
        }
    }
}

このbashコマンドを実行して、機能することを確認します。

for i in {1..1000}
do
Java FileWriterTest test.txt $i 10 true &
done

(出力から)10msごとに1回だけ書き込みが行われ、最終的にすべての数値がファイルに存在することがわかります。

出力:

/tmp wc -l test.txt
1000 test.txt
/tmp

ロックなしの同じテストでは、データが失われていることが示されています。

for i in {1..1000}
do
Java FileWriterTest test.txt $i 10 false &
done

出力:

/tmp wc -l test.txt
764 test.txt
/tmp

代わりにtryLockをテストするように簡単に変更できるはずです。

0
Davi Cavalcanti