2

「データ」オブジェクトをファイルにシリアル化する複数のスレッドがあります。ファイル名は、オブジェクトの 2 つのフィールドに基づいています

  クラスデータ{
    org.joda.DateTime 時間;
    文字列のタイトル。

    public String getFilename() {
      return time.toString() + '_' + タイトル + ".xml";
    }

2 つのデータ オブジェクトが同じ「時間」と「タイトル」を持ち、同じファイル名になる可能性があります。

これは許容範囲であり、どちらかが救われることを嬉しく思います。(それらが同じである場合、それらはおそらく同じ Data オブジェクトです)

私の問題は、2 つ (またはそれ以上) のスレッドが同時にファイルに書き込みを行っているため、不正な形式の XML が発生することです。

java.nio.channels.FileLock を見てみましたが、これは VM 全体のロック用であり、特にスレッド内ロックには適していません。

DataIO.class で同期できます (ただし、実際には個々のファイルでのみ同期したいので、これは大きなオーバーヘッドを引き起こします)。

複数の File オブジェクトが同じ System-File を表すことができるため、File オブジェクトでの同期は役に立ちません。

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

クラスDataIO {
  public void writeArticleToFile(Article article, String filename, boolean overwrite) throws IOException {
    ファイル file = new File(ファイル名);
    writeArticleToFile(記事、ファイル、上書き);
  }

  public void writeDataToFile(Data data, File file, boolean overwrite) throws IOException {
    if (file.exists()) {
      if (上書き) {
        if (!file.delete()) {
          throw new IOException("上書きのため、ファイルの削除に失敗しました: " + file);
        }
      } そうしないと {
        throw new IOException("ファイル " + ファイル + " が既に存在し、上書きフラグが false に設定されています。");
      }
    }

    ファイルparentFile = file.getParentFile();
    if (parentFile != null) {
      file.getParentFile().mkdirs();
    }

    file.createNewFile();

    if (!file.canWrite()) {
      throw new IOException("ファイルに書き込む権限がありません: " + file);
    }

    FileOutputStream fos = new FileOutputStream(file, false);
    試す {
      writeDataToStream(データ、fos);
      logger.debug("記事をファイルに書き込みました: " + file.getAbsolutePath());
    } 最後に {
      fos.close();
    }
  }
}
4

3 に答える 3

2

これを正しく読んでいれば、単一のファイルを表す Data オブジェクトがあります。

Data オブジェクトに基づいてストライプ セットを作成することを検討できます。の ConcurrentHashMap を持つ可能性があります

ConcurrentMap<Data,Lock> lockMap = new ConcurrentHashMap<Data,Lock>();

いいえ、このオブジェクトに書き込みたい場合は、次のことができます。

Lock lock = lockMap.get(someMyDataObject);
lock.lock();
try{
   //write object here
}finally{
   lock.unlock();
}

タイトルと DateTime に基づいて hashCode と equals メソッドを記述する必要があることに注意してください。

于 2010-08-04T15:20:02.857 に答える
1

ファイル名である文字列をインターン()できます。次に、interned 文字列で同期します。

class DataIO {
  public void writeArticleToFile(Article article, String filename, boolean overwrite) throws IOException {
    synchronized(filename.intern()) {
       File file = new File(filename);
       writeArticleToFile(article, file, overwrite);
    }
  }
于 2010-08-04T15:16:36.937 に答える
0

同期を使用することが使用すべき手法であることに同意します。必要なのは、ファイル順列ごとに個別のオブジェクトであり、さらに重要なことに、毎回同じオブジェクトです。1 つのオプションは、FileLock というクラスを作成することです。

public class FileLock {
    DateTime time;
    String title;

    public FileLock(DateTime time, String title) {
        this.time = time;
        this.title = title;
    }

    override equals/hashCode based on those two properties

    static Hashtable<FileLock, FileLock> unqiueLocks = new Hashtable<FileLock, FileLock>();
    static lockObject = new Object();

    public static FileLock getLock(DateTime time, String title) {
        synchronized (lockObject) {
            FileLock lock = new FileLock(time, title);
            if (unqiueLocks.ContainsKey(lock)) {
                return unqiueLocks.get(lock);
            }
            else {
                unqiueLocks.put(lock, lock);
                return lock;
            }
        }
    }
}

次に、呼び出し元は次のように使用します。

synchronized (FileLock.getLock(time, title)) {
    ...
}

ハッシュテーブルは新しいファイル/時間順列で成長し続けるため、これにはメモリリークがあることに注意してください。必要に応じて、この手法を変更して、getLock の呼び出し元が、Hashtable をクリーンに保つために使用する releaseLock メソッドも呼び出すようにすることができます。

于 2010-08-04T15:31:02.743 に答える