Question

J'ai plusieurs fils qui sont sérialisation mes objets « données » aux fichiers. Le nom de fichier est basé sur 2 champs de l'objet

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

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

Il est possible que 2 objets de données auront le même « temps » et « titre », et ainsi le même nom.

Ceci est acceptable, et je suis heureux pour soit être sauvé. (Ils sont probablement les mêmes données objet de toute façon si ce sont les mêmes)

Mon problème est que deux threads sont en train d'écrire dans un fichier (ou plus), EN MÊME TEMPS, provoquant XML malformé.

J'ai eu un coup d'œil à java.nio.channels.FileLock, mais il est pour le verrouillage VM-large, et pas spécifiquement adapté pour le verrouillage intra-fil.

Je pourrais synchroniser sur DataIO.class (mais qui va provoquer une surcharge énorme, car je veux vraiment que synchroniser sur le fichier individuel).

Synchronisation sur l'objet fichier sera inutile, car plusieurs objets de fichiers peuvent représenter le même système de fichiers.

Code suit:

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();
    }
  }
}
Était-ce utile?

La solution

stagiaire pourrait () la chaîne qui est le nom du fichier. Synchronisez ensuite sur la chaîne internées.

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

Autres conseils

Si je lis correctement ce que vous avez un objet de données qui représente un seul fichier.

Vous pouvez créer un ensemble rayé basé sur l'objet de données. Eventuellement ayant une ConcurrentHashMap de

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

Non lorsque vous voulez écrire, vous pouvez le faire à cet objet:

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

Gardez à l'esprit que vous devez écrire le hashCode et égale méthode basée sur le titre et DateTime

Je suis d'accord que l'utilisation de la synchronisation est la technique que vous devez utiliser. Qu'est-ce que vous avez besoin est un objet distinct pour chaque permutation de fichiers, et plus important encore le même objet à chaque fois. Une option pourrait être de créer une classe appelée 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;
            }
        }
    }
}

Ensuite, les appelants utiliseraient comme:

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

Gardez à l'esprit ce qui a une fuite de mémoire depuis le Hashtable ne cesse de croître avec de nouvelles permutations fichier / temps. Si vous avez besoin, vous pouvez modifier cette technique afin que les appelants de getLock également invoquer une méthode de releaseLock que vous utilisez pour garder le Hashtable propre.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top