2

我有多个线程将我的“数据”对象序列化为文件。文件名基于对象中的 2 个字段

  类数据{
    org.joda.DateTime 时间;
    字符串标题;

    公共字符串 getFilename() {
      返回 time.toString() + '_' + 标题 + ".xml";
    }

2 个 Data 对象可能具有相同的“时间”和“标题”,因此文件名也相同。

这是可以接受的,我很高兴两者都得救。(如果它们相同,它们可能是相同的 Data 对象)

我的问题是两个(或更多)线程同时写入文件,导致 XML 格式错误。

我查看了 java.nio.channels.FileLock,但它适用于 VM-Wide 锁定,特别不适合线程内锁定。

我可以在 DataIO.class 上同步(但这会导致巨大的开销,因为我真的只想在单个文件上同步)。

对 File 对象进行同步是没有用的,因为多个 File 对象可以代表同一个 System-File。

代码如下:

类数据IO {
  公共无效 writeArticleToFile(文章文章,字符串文件名,布尔覆盖)抛出 IOException {
    文件文件 = 新文件(文件名);
    writeArticleToFile(文章,文件,覆盖);
  }

  public void writeDataToFile(Data data, File file, boolean overwrite) throws IOException {
    如果 (file.exists()) {
      如果(覆盖){
        如果(!file.delete()){
          throw new IOException("删除文件失败,覆盖:" + file);
        }
      } 别的 {
        throw new IOException("文件 " + 文件 + " 已经存在,并且覆盖标志设置为 false。");
      }
    }

    文件 parentFile = file.getParentFile();
    如果(父文件!= null){
      file.getParentFile().mkdirs();
    }

    文件.createNewFile();

    如果(!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

您可以 intern() 作为文件名的字符串。然后在实习字符串上同步。

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

我同意使用同步是您应该使用的技术。您需要的是每个文件排列的不同对象,更重要的是每次都是相同的对象。一种选择可能是创建一个名为 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 会随着新的文件/时间排列而不断增长。如果需要,您可以修改此技术,以便 getLock 的调用者也调用您用来保持 Hashtable 清洁的 releaseLock 方法。

于 2010-08-04T15:31:02.743 回答