web-dev-qa-db-fra.com

java - Le processus ne peut pas accéder au fichier car il est utilisé par un autre processus

J'ai un morceau de code qui surveille un répertoire pour l'ajout de fichiers. Chaque fois qu'un nouveau fichier est ajouté au répertoire, le contenu du fichier est sélectionné et publié sur kafka, puis le fichier est supprimé. 

Cela fonctionne lorsque je fais une demande unique mais dès que je soumets mon code à 5 ou 10 demandes d'utilisateurs de jMeter, le contenu est publié sur kafka mais le code ne peut pas supprimer le fichier. Je reçois une FileSystemException avec un message que The process cannot access the file because it is being used by another process..

Je suppose qu'il y a un problème de concurrence que je suis incapable de voir.

public void monitor() throws IOException, InterruptedException {
    Path faxFolder = Paths.get(TEMP_FILE_LOCATION);
    WatchService watchService = FileSystems.getDefault().newWatchService();
    faxFolder.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
    boolean valid = true;
    do {
        WatchKey watchKey = watchService.take();
        for (WatchEvent<?> event : watchKey.pollEvents()) {
            if (StandardWatchEventKinds.ENTRY_CREATE.equals(event.kind())) {
                String fileName = event.context().toString();
                publishToKafka(new File(TEMP_FILE_LOCATION + fileName).toPath(), "topic");
            }
        }
        valid = watchKey.reset();
    } while (valid);
}

private void publishToKafka(Path path, String topic) {
    try (BufferedReader reader = Files.newBufferedReader(path)) {
        String input = null;
        while ((input = reader.readLine()) != null) {
            kafkaProducer.publishMessageOnTopic(input, topic);
        }
    } catch (IOException e) {
        LOG.error("Could not read buffered file to send message on kafka.", e);
    } finally {
        try {
            Files.deleteIfExists(path); // This is where I get the exception
        } catch (IOException e) {
            LOG.error("Problem in deleting the buffered file {}.", path.getFileName(), e);
        }
    }
}

Journal des exceptions: 

Java.nio.file.FileSystemException: D:\upload\notif-1479974962595.csv: The process cannot access the file because it is being used by another process.

    at Sun.nio.fs.WindowsException.translateToIOException(Unknown Source)
    at Sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
    at Sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
    at Sun.nio.fs.WindowsFileSystemProvider.implDelete(Unknown Source)
    at Sun.nio.fs.AbstractFileSystemProvider.deleteIfExists(Unknown Source)
    at Java.nio.file.Files.deleteIfExists(Unknown Source)
    at com.panasonic.mdw.core.utils.MonitorDirectory$FileContentPublisher.publishToKafka(MonitorDirectory.Java:193)
    at com.panasonic.mdw.core.utils.MonitorDirectory$FileContentPublisher.sendData(MonitorDirectory.Java:125)
    at com.panasonic.mdw.core.utils.MonitorDirectory$FileContentPublisher.run(MonitorDirectory.Java:113)
    at Java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at Java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at Java.lang.Thread.run(Unknown Source)
4
Furquan Ahmed

En regardant votre code, il semble que lorsqu'un fichier est sélectionné par un thread pour être publié à nouveau, un autre thread le récupère pour publication. C'est pourquoi personne ne peut le supprimer . Il doit s'agir uniquement d'un problème de concurrence. Vous devez redéfinir le code en fonction de critère: les étapes qui peuvent être exécutées simultanément et celles qui ne peuvent pas l'être. Les étapes du processus complet sont les suivantes:

  1. ramasser un fichier (le thread principal devrait le faire)
  2. publier un fichier (appeler un autre thread pour le faire)
  3. supprimer le fichier (le fil appelé doit le supprimer)
  4. vérifie si un fichier est présent (à nouveau le thread principal peut le faire)

De plus, au moment où un fichier est sélectionné, vous pouvez le lire dans le tampon, le supprimer, puis continuer avec la publication. Cela garantira que le thread principal n'affecte pas ce fichier à un autre thread.

2
grsdev7

J'avais un problème similaire ainsi que dans le fil suivant: Multithreading sur Queue Lorsque j'essayais de télécharger des fichiers créés dynamiquement vers un service de file d'attente et que cela prenait 2 jours à résoudre. Merci à Holger qui a donné la réponse ci-dessus, selon laquelle le verrouillage se produit peut-être parce que la création n’a pas été entièrement réalisée à la lecture d’un autre fil de discussion, cela m’a fait gagner beaucoup de temps.

Ma solution initiale, souvent trouvée sur Internet, était la suivante:

WatchEvent<Path> ev = cast(event);
Path name = ev.context();
Path child = dir.resolve(name);
//queueUploadFile(child);
if (kind == ENTRY_CREATE) {
    uploadToQueue(this.queueId, child);
}

Je l'ai changé pour: 

WatchEvent<Path> ev = cast(event);
Path name = ev.context();
Path child = dir.resolve(name);
//queueUploadFile(child);
if (kind == ENTRY_MODIFY) {
    uploadToQueue(this.queueId, child);
}

Et tout fonctionne parfaitement. Pour gérer le "en quelque sorte" plusieurs événements ENTRY_MODIFY qui se déclenchent (fichiers dupliqués téléchargés), j'effectue la suppression du fichier dans la méthode uploadToQueue () une fois qu'il est téléchargé.

J'espère que mon approche adoptée sur la base de la contribution ci-dessus aidera également d'autres personnes aux prises avec un problème similaire.

0
I Made Putrama