write
データをファイルに安全に書き込むことになっているメソッドがあります。
// The current file I am writing to.
FileOutputStream file = null;
...
// Synchronized version.
private void write(byte[] bytes) {
if (file != null && file.getChannel() != null) {
try {
boolean written = false;
do {
try {
// Lock it!
FileLock lock = file.getChannel().lock();
try {
// Write the bytes.
file.write(bytes);
written = true;
} finally {
// Release the lock.
lock.release();
}
} catch (OverlappingFileLockException ofle) {
try {
// Wait a bit
Thread.sleep(0);
} catch (InterruptedException ex) {
throw new InterruptedIOException("Interrupted waiting for a file lock.");
}
}
} while (!written);
} catch (IOException ex) {
log.warn("Failed to lock " + fileName, ex);
}
} else {
log.warn("Failing - " + (file == null ? "file" : "channel") + " is null!!");
}
}
しわがあることはわかっていますが、しばらくは問題なく動作しています。
最近、このコードを使用してJava 5(Java 6から)でビルドおよび実行するプロジェクトを変更しましたが、ファイルのロックを待ってデッドロックしているように見えます。これはマルチスレッドアプリであり、複数のスレッドが同じファイルに書き込もうとする可能性があります。
デバッガーは、ハングしたスレッドがFileLock lock = file.getChannel().lock()
呼び出しが戻るのを待っていることを通知します。
いくつかの研究は、次のような興味深い小さなナゲットを生み出しました。
ファイルロックは、Java仮想マシン全体に代わって保持されます。同じ仮想マシン内の複数のスレッドによるファイルへのアクセスを制御するのには適していません。
だから私はそれを間違っていますか?もしそうなら、正しい方法は何ですか?私がそれを正しくやっているのなら、どうして私はデッドロックに陥るのですか?
追加:言及するのを忘れました-各スレッドはこのオブジェクトの独自のコピーを保持しているため、コード内で同期の問題が発生することはありません。FileChannel.lock()
書き込みがインターリーブしないようにするために、この方法に頼っても安全だと感じました。
追加も:私は確かにさまざまなsynchronized
メカニズムを使用して問題を解決しました。ただし、未解決の質問があります。
- なぜ
FileLock lock = file.getChannel().lock();
適していないのか...? - すべてがJava-6で正常に機能したのに、Java-5に切り替えたときにのみ問題が発生したのはなぜですか?