Question

I ai deux fils indépendants F1 et F2 (pour être précis, deux instances de java.util.concurrent.FutureTask) qui sont exécutés en parallèle.

F1 Effectuer un traitement, puis copier le résultat dans un fichier XML. Il répète ensuite ces étapes jusqu'à ce qu'il n'a rien à voir (de nombreux fichiers XML sont créés). F2 regarde dans le répertoire de sortie de F1, et prendre un fichier, l'analyser et d'exécuter un certain traitement sur elle.

Cela fonctionne tout à fait assez bien, sauf que parfois, F2 obtient des données XML à partir du fichier tronqué. Je veux dire par là un XML incomplet, où un nœud XML ne sont pas présents. Le problème est que ce n'est pas toujours reproductible, et les fichiers qui sont tronqués ne sont pas toujours les mêmes. À cause de cela, je pense que si F1 est en train d'écrire un fichier sur le disque, F2 tente de lire le même fichier. Voilà pourquoi parfois je reçois ce genre d'erreur.

Ma question : Je me demande s'il y a un mécanisme qui se verrouille (même pour la lecture) le fichier F1 est en train d'écrire jusqu'à ce qu'il ait complètement fini de l'écrire sur le disque, donc F2 ne sera pas être capable de lire jusqu'à ce que le fichier est déverrouillé. Ou tout autre moyen de résoudre mon problème sera la bienvenue!

F1 est en train d'écrire le fichier de cette façon:

try {
    file = new File("some-file.xml");
    FileUtils.writeStringToFile(file, xmlDataAsString);
} catch (IOException ioe) {
    LOGGER.error("Error occurred while storing the XML in a file.", ioe);
}

F2 est en train de lire le fichier de cette façon:

private File getNextFileToMap() {
    File path = getPath(); // Returns the directory where F1 stores the results...
    File[] files = path.listFiles(new FilenameFilter() {
        public boolean accept(File file, String name) {
            return name.toLowerCase().endsWith(".xml");
        }
    });
    if (files.length > 0) {
        return files[0];
    }
    return null;
}

// Somewhere in my main method of F2
...
f = getNextFileToMap();
Node xmlNode = null;
try {
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = factory.newDocumentBuilder();
    Document doc = builder.parse(f);
    if (doc != null) {
        xmlNode = doc.getDocumentElement();
    }
} catch (Exception e) {
    LOGGER.error("Error while getting the XML from the file " + f.getAbsolutePath(), e);
}
Était-ce utile?

La solution

Puisque vous déjà le filtrage des fichiers .xml dans F2, ont sortie de F1 dans un fichier .temp, puis le renommer en .xml comme une étape finale. De cette façon, F2 ignorera le fichier F1 fait jusqu'à ce que F1 est complètement fait avec.

Autres conseils

Avez-vous regardé java.nio.channels.FileLock API?

Une solution plus simple est peut-être d'écrire un nom différent (par exemple foo.tmp), puis le renommer (par exemple pour foo.xml) quand il est prêt - le changement de nom est atomique sur la plupart des systèmes d'exploitation (dans un répertoire), alors quand l'autre processus voit le fichier XML il doit être complet. Ceci est susceptible d'être beaucoup plus simple que le verrouillage.

Utilisez le mot-clé synchronisé sur une commune objet, un objet de fichier pointant vers le fichier serait le meilleur dans ce cas:

 class MyFileFactory {
     private static HashMap<string,File> files = new HashMap<String,File>();
     public static File get(String name) {
         if (!files.keyExists(name)) {
              files.put(name, new File(name));
         }
         return files.get(name);
     }
 }

 // Another class
 synchronized(File f = MyFileFactory::get("some-file.xml")) {
      // read or write here
 }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top