VM内の個々のスレッドのためにファイルをロックします
-
25-09-2019 - |
質問
私は、ファイルに私の「データ」のオブジェクトをシリアル化されている複数のスレッドを持っています。ファイル名はObjectから2つのフィールドに基づいています。
class Data { org.joda.DateTime time; String title; public String getFilename() { return time.toString() + '_' + title + ".xml"; }
これは、2つのデータオブジェクトが同じ「時間」と「タイトル」を持っていること、および同じファイル名ので可能です。
これは許容可能である、と私はどちらかを保存するために満足しています。 (それらが同じであるなら、彼らはとにかく、おそらく同じデータオブジェクトです)。
私の問題は、2つ(またはそれ以上)のスレッドが不正な形式のXMLを引き起こし、同時にファイルに書き込みをしているということです。
私はjava.nio.channels.FileLockを見ていたが、それはVM-ワイドロックのためだ、とスレッド内のロックのために特に適していません。
私はDataIO.classに同期させることができます(ただし、私は実際には、個々のファイルに同期させたいので、それは、巨大なオーバーヘッドが発生します)。
は、ファイルオブジェクトの同期は、複数のファイルオブジェクトが同一のシステム・ファイルを表すことができるように、無駄になります。
コード従います:
class DataIO { public void writeArticleToFile(Article article, String filename, boolean overwrite) throws IOException { File file = new File(filename); writeArticleToFile(article, file, overwrite); } public void writeDataToFile(Data data, File file, boolean overwrite) throws IOException { if (file.exists()) { if (overwrite) { if (!file.delete()) { throw new IOException("Failed to delete the file, for overwriting: " + file); } } else { throw new IOException("File " + file + " already exists, and overwrite flag is set to false."); } } File parentFile = file.getParentFile(); if (parentFile != null) { file.getParentFile().mkdirs(); } file.createNewFile(); if (!file.canWrite()) { throw new IOException("You do not have permission to write to the file: " + file); } FileOutputStream fos = new FileOutputStream(file, false); try { writeDataToStream(data, fos); logger.debug("Successfully wrote Article to file: " + file.getAbsolutePath()); } finally { fos.close(); } } }
解決
あなたは可能性がインターン()ファイル名である文字列。そして、インターンの文字列で同期ます。
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);
}
}
他のヒント
私はこれを正しく読んでいる場合は、1つのファイルを表すデータオブジェクトを持っています。
あなたは、データオブジェクトに基づいてストライプセットを作成することを検討することができます。おそらくののConcurrentHashMapを持つ
ConcurrentMap<Data,Lock> lockMap = new ConcurrentHashMap<Data,Lock>();
はありません、あなたが行うことができます。このオブジェクトに書き込みたいときます:
Lock lock = lockMap.get(someMyDataObject);
lock.lock();
try{
//write object here
}finally{
lock.unlock();
}
あなたはハッシュコードを記述する必要があります覚えておいてくださいとタイトルに基づいてequalsメソッドとのDateTime
私は、同期を使用すると、使用すべき技術であることに同意します。何が必要たびに、各ファイルの順列のための明確な対象に、より重要な同じオブジェクトです。 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)) {
...
}
Hashtableの新しいファイル/時間の順列で成長し続けているので、これはメモリリークが発生しているに注意してください。必要であれば、あなたはHashtableの清潔を保つために使用することreleaseLockのメソッドInvokeもGETLOCKの発信者ので、この手法を変更することができます。