Frage

Ich habe mehrere Threads, die meine ‚Daten‘ Objekte Dateien werden serialisiert. Der Dateiname basiert auf zwei Felder aus dem Objekt

  class Data {
    org.joda.DateTime time;
    String title;

    public String getFilename() {
      return time.toString() + '_' + title + ".xml";
    }

Es ist möglich, dass zwei Datenobjekte die gleiche ‚Zeit‘ haben und ‚Titel‘, und so den gleichen Dateinamen.

Dies ist akzeptabel, und ich bin glücklich für entweder gespeichert werden. (Sie sind wahrscheinlich das gleiche Datenobjekt trotzdem, wenn diejenigen sind gleich)

Mein Problem ist, dass zwei (oder mehr) Threads in eine Datei ZUGLEICH schreiben, fehlerhafte XML verursacht.

hatte ich einen Blick auf java.nio.channels.FileLock, aber es ist für VM-Wide-Verriegelung, insbesondere nicht geeignet für intra-Gewindesicherung.

ich auf DataIO.class synchronisieren könnte (aber das wird einen großen Aufwand verursachen, da ich wirklich nur auf der einzelnen Datei synchronisiert werden soll).

Synchronisieren auf dem File-Objekt wird nutzlos sein, da mehrere File-Objekte die gleiche System-Datei darstellen können.

-Code folgt:

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();
    }
  }
}
War es hilfreich?

Lösung

Sie könnten intern () die Zeichenfolge, die den Dateinamen. Dann synchronisieren auf dem internierten String.

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);
    }
  }

Andere Tipps

Wenn ich das Lesen ich das richtig haben Sie ein Datenobjekt, das eine einzelne Datei darstellt.

Sie können erwägen, einen gestreiften Satz auf dem Datenobjekt basierend zu schaffen. Möglicherweise mit einem ConcurrentHashMap von

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

Nein, wenn Sie schreiben, um dieses Objekt möchten Sie tun können:

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

Beachten Sie würden Sie die hashCode schreiben müssen und gleich Methode basiert auf dem Titel und Datetime

Ich bin damit einverstanden, dass die Synchronisation mit der Technik ist, die Sie verwenden sollen. Was Sie brauchen, ist ein separates Objekt für jede Datei Permutation, und was noch wichtiger ist das gleiche Objekt jedes Mal. Eine Option könnte sein, eine Klasse zu erstellen, genannt 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;
            }
        }
    }
}

Dann würden Anrufer es wie verwenden:

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

Beachten Sie dies ein Speicherleck hat, da die Hashtable wächst mit neuen Permutationen Datei / Zeit hält. Wenn es nötig ist, können Sie diese Technik modifizieren, so dass Anrufer von GETLOCK auch invoke einer releaseLock Methode, dass Sie die Hashtable sauber zu halten verwenden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top