Javaを使用してファイルをロックする方法(可能な場合)


121

FileReaderを使用してファイルを開くJavaプロセスがあります。別の(Java)プロセスがこのファイルを開かないようにするには、または少なくとも2番目のプロセスにファイルが既に開かれていることを通知するにはどうすればよいですか?これは、ファイルが開いている場合(これが私の問題を解決します)、自動的に2番目のプロセスに例外を発生させますか、それとも何らかのフラグまたは引数を使用して最初のプロセスで明示的に開く必要がありますか?

明確にするために:

フォルダーをリストし、それを処理するためにリスト内の各ファイルを開くJavaアプリがあります。各ファイルを順番に処理します。各ファイルの処理は、それを読み取り、内容に基づいていくつかの計算を行うことで構成され、約2分かかります。同じことを行いますが、代わりにファイルに書き込む別のJavaアプリもあります。これらのアプリを同時に実行できるようにしたいので、シナリオは次のようになります。ReadAppはフォルダーを一覧表示し、ファイルA、B、Cを見つけます。ファイルAを開き、読み取りを開始します。WriteAppはフォルダーを一覧表示し、ファイルA、B、Cを見つけます。ファイルAを開き、それが(例外またはその他の方法で)開いていることを確認して、ファイルBに移動します。ReadAppはファイルAを終了し、Bに進みます。はオープンでCに続きます。WriteAppが t ReadAppが同じファイルを読み取っている間に書き込み、またはその逆。それらは異なるプロセスです。


12
インプロセス(2つのJVM)またはスレッド(同じJVM)の「プロセス」を意味しますか。答えへの影響は最も重要です。
Stu Thompson

ここで解決策を示すサンプルコードを確認してください:stackoverflow.com/a/58871479/5154619
Davi Cavalcanti

回答:


117

FileChannel.lockはおそらく必要なものです。

try (
    FileInputStream in = new FileInputStream(file);
    java.nio.channels.FileLock lock = in.getChannel().lock();
    Reader reader = new InputStreamReader(in, charset)
) {
    ...
}

(免責事項:コードはコンパイルされておらず、確かにテストされていません。)

FileLockのAPIドキュメントの「プラットフォームの依存関係」というタイトルのセクションに注意してください。


22
さらに重要なこととして、JVMのロックは、単一のJVM内の個々のスレッドによるアクセスのためにファイルをロックするのには適していないことを理解してください。
Stu Thompson

11
書き込み可能なストリーム(つまりFileOutputStream)が必要です。
ハビエル

@Javierしますか?私は試していません。それが要件であると言っているAPIドキュメントから飛び出すものはありません。FileOutputStreamにはあまり役に立ちませんReader
トムホーティン-タックライン

18
はい、私はそれを試してみましたが、それがスローNonWritableChannelExceptionので、lock()試みは排他ロックを取得するために、それは、書き込みアクセスが必要です。入力ストリームがある場合は、を使用しlock(0L, Long.MAX_VALUE, false)て、共有ロックを取得し、読み取りアクセスのみが必要です。RandomAccessFile読み取り中に排他ロックが必要な場合は、opened in read-writeモードを使用することもできますが、同時読み取りは禁止されます。
ハビエル

6
@ハビエル私はあなたが言うつもりだと思うlock(0L, Long.MAX_VALUE, true)、ではないlock(0L, Long.MAX_VALUE, false)。最後の引数はboolean shared docs.oracle.com/javase/8/docs/api/java/nio/channels/…
ジョンサリバン

60

java.ioパッケージ内のクラスを使用せず、代わりにパッケージを使用してくださいjava.nio。後者にはFileLockクラスがあります。にロックを適用できますFileChannel

 try {
        // Get a file channel for the file
        File file = new File("filename");
        FileChannel channel = new RandomAccessFile(file, "rw").getChannel();

        // Use the file channel to create a lock on the file.
        // This method blocks until it can retrieve the lock.
        FileLock lock = channel.lock();

        /*
           use channel.lock OR channel.tryLock();
        */

        // Try acquiring the lock without blocking. This method returns
        // null or throws an exception if the file is already locked.
        try {
            lock = channel.tryLock();
        } catch (OverlappingFileLockException e) {
            // File is already locked in this thread or virtual machine
        }

        // Release the lock - if it is not null!
        if( lock != null ) {
            lock.release();
        }

        // Close the file
        channel.close();
    } catch (Exception e) {
    }

ところで、私はこのヒントstackoverflow.com/a/35885/1422630から現在のPIDをロックファイルに書き込んでいるので、新しいインスタンスでそれを読み取ることができます。
Aquarius Power

1
これは見た目は良いのですが、うまくいきません。ファイルが存在していなくても毎回OverlappingFileLockExceptionが発生します
Gavriel

1
それは例に書かれているようあなたがロックした後のtryLockを呼び出すと、問題が起こるのだろう
イゴールVukovićを

17

Java NIOJDK 1.4以上)を使用できる場合は、あなたが探していると思いますjava.nio.channels.FileChannel.lock()

FileChannel.lock()


5
多分。「プロセス」が意味するOPに依存します。「ファイルロックは、Java仮想マシン全体に代わって保持されます。同じ仮想マシン内の複数のスレッドによるファイルへのアクセスの制御には適していません。」
Stu Thompson

@Stu:ずっと前にこの質問に回答したことはわかっていますが、言ったときの意味を詳しく説明できれば幸いですFile locks are held on behalf of the entire Java virtual machine. They are not suitable for controlling access to a file by multiple threads within the same virtual machine
Thang Pham

3
@Harry彼はドキュメントから引用していますdownload.oracle.com/javase/6/docs/api/java/nio/channels/…これは、スレッドからは見えないが、他のプロセスに影響を与えることを意味します。
Artur Czajka

@Harry:これらのネクロコメントにさらに追加するために、TomcatでWebサイトを提供するためにJavaを使用していると想像してください。多数のスレッドがあり、それぞれがWebブラウザーからの1つの要求を処理する場合があります。ただし、それらはすべて、キッチンの料理人が多すぎるように、同じファイルロックメカニズムを制御します。1つのリクエストが2番目のリクエストの途中で終了し、途中でファイルが突然「ロック解除」され、cronjobなどの他のプロセスがそれをロックして、予期せずにロックし、リクエストを完了できません...
ダリアン


5

これはあなたが探しているものではないかもしれませんが、別の角度から問題に直面するために...

これらの2つのJavaプロセスは、同じアプリケーションの同じファイルにアクセスする可能性がありますか?おそらく、ファイルへのすべてのアクセスを、単一の同期された方法で(あるいは、JSR-166を使用してさらに)フィルタリングすることができますか?そうすれば、ファイルへのアクセスを制御したり、アクセス要求をキューに入れたりすることができます。


3
2つのプロセスは同期を使用できません。同じプロセス内の2つのスレッドのみです。
ローン侯爵、2015年

3

RandomAccessFileを使用してチャネルを取得し、lock()を呼び出します。入力または出力ストリームによって提供されるチャネルには、適切にロックするための十分な権限がありません。必ずfinallyブロックでunlock()を呼び出してください(ファイルを閉じてもロックが解放されるとは限りません)。


詳しく説明できますか?つまり、RandomAccessファイルによるロックは、ストリーム1よりも優れているか、より安全です
Paralife '25

以下に掲載されている簡単な例へのリンク
Touko '27

パラライフ-遅れて申し訳ありません-あなたの質問に気づきました。ストリームからのロックは、読み取りロック(入力ストリームの場合)および排他的なフルチャネル書き込みロック(出力ストリームの場合)になります。私の経験では、RAFからのロックにより、より細かい制御が可能になります(つまり、ファイルの一部をロックできます)。
ケビン日

1

以下は、JVMによってプロセスが完了するまでファイルをロックするサンプルスニペットコードです。

 public static void main(String[] args) throws InterruptedException {
    File file = new File(FILE_FULL_PATH_NAME);
    RandomAccessFile in = null;
    try {
        in = new RandomAccessFile(file, "rw");
        FileLock lock = in.getChannel().lock();
        try {

            while (in.read() != -1) {
                System.out.println(in.readLine());
            }
        } finally {
            lock.release();
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        try {
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.