4

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メカニズムを使用して問題を解決しました。ただし、未解決の質問があります。

  1. なぜFileLock lock = file.getChannel().lock(); 適していないのか...
  2. すべてがJava-6で正常に機能したのに、Java-5に切り替えたときにのみ問題が発生したのはなぜですか?
4

2 に答える 2

4

FileLockプロセス間ロック専用です。javadocは次のように読み取ります。

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

Java スレッド (同じ JVM) 間でロックするには、共有ロックを使用する必要があります。ファイル書き込みクラス内で同期ブロックを使用することをお勧めします (これらの記事によると、これが最適に機能する可能性があります)。

final Object lock = new Object();

public void write(...){
  synchronized(lock){
    // do writing
  }
}

もう 1 つのアプローチは、ReentrantLockを使用してから、実証済みのイディオムを使用することです。

final ReentrantLock lock = new ReentrantLock();

public void write(...){
  try {
    lock.lock()
    // do the writing
  } finally {
    // forget this and you're screwed
    lock.unlock();
  }
}
于 2012-07-18T15:20:48.347 に答える
1

ハッシュマップではなく、ファイルを使用して実際のコードにクリティカルセクションの概念を実装する必要がある場合があります。同期ブロックを作成するか、ファイルアクセスコードを別のプロシージャに分割して、そのメソッドを同期させることができます。

基本的に、一度に1つのスレッドのみが同期ブロックを実行します。それはあなたが必要とする排他的なアクセスをあなたに与えます。

これを行う別の方法は、機能要件に応じて、シリアルスレッドエグゼキュータを使用することです。

このスレッドを確認することをお勧めします: Javaを使用して共有フォルダー内のファイルアクセスを同期する方法(または:ネットワークレベルでReadWriteLock)

于 2012-07-18T14:44:11.070 に答える