Comment puis-je verrouiller un fichier en utilisant java (si possible)

121

J'ai un processus Java qui ouvre un fichier à l'aide d'un FileReader. Comment puis-je empêcher un autre processus (Java) d'ouvrir ce fichier, ou au moins informer ce deuxième processus que le fichier est déjà ouvert? Cela fait-il automatiquement au deuxième processus une exception si le fichier est ouvert (ce qui résout mon problème) ou dois-je l'ouvrir explicitement dans le premier processus avec une sorte d'indicateur ou d'argument?

Clarifier:

J'ai une application Java qui répertorie un dossier et ouvre chaque fichier dans la liste pour le traiter. Il traite chaque fichier après l'autre. Le traitement de chaque fichier consiste à le lire et à faire quelques calculs en fonction du contenu et cela prend environ 2 minutes. J'ai également une autre application Java qui fait la même chose mais qui écrit à la place sur le fichier. Ce que je veux, c'est pouvoir exécuter ces applications en même temps pour que le scénario se déroule comme suit. ReadApp répertorie le dossier et trouve les fichiers A, B, C. Il ouvre le fichier A et démarre la lecture. WriteApp répertorie le dossier et trouve les fichiers A, B, C.Il ouvre le fichier A, voit qu'il est ouvert (par une exception ou de quelque manière que ce soit) et va au fichier B.ReadApp termine le fichier A et continue vers B.Il voit qu'il est ouvert et continue vers C. Il est crucial que WriteApp ne le fasse pas t écrire pendant que ReadApp lit le même fichier ou vice versa. Ce sont des processus différents.

Paralife
la source
12
Voulez-vous dire «processus» comme en cours (deux JVM) ou thread (même JVM). L'impact sur la réponse est primordial.
Stu Thompson
Vérifiez l'exemple de code montrant la solution ici: stackoverflow.com/a/58871479/5154619
Davi Cavalcanti

Réponses:

117

FileChannel.lock est probablement ce que vous voulez.

try (
    FileInputStream in = new FileInputStream(file);
    java.nio.channels.FileLock lock = in.getChannel().lock();
    Reader reader = new InputStreamReader(in, charset)
) {
    ...
}

(Avertissement: Code non compilé et certainement pas testé.)

Notez la section intitulée "dépendances de la plate-forme" dans le document API pour FileLock .

Tom Hawtin - ligne de pêche
la source
22
Plus important encore, comprenez que le verrou est pour la JVM, et ne convient pas pour verrouiller le fichier pour l'accès par des threads individuels au sein d'une seule JVM.
Stu Thompson
11
Vous avez besoin d'un flux accessible en écriture (ie FileOutputStream).
Javier
@Javier et toi? Je n'ai pas essayé. Rien ne sort de la documentation de l'API indiquant que c'est une exigence. FileOutputStreamne sera pas très utile pour un Reader.
Tom Hawtin - tackline
18
Oui, je l'ai essayé et ça jette NonWritableChannelException, car lock()tente d'acquérir un verrou exclusif, mais cela nécessite un accès en écriture. Si vous avez un flux d' entrée , vous pouvez utiliser lock(0L, Long.MAX_VALUE, false)qui acquiert un verrou partagé et ne nécessite qu'un accès en lecture. Vous pouvez également utiliser un RandomAccessFileouvert en mode lecture-écriture si vous souhaitez un verrou exclusif lors de la lecture ... mais cela interdirait les lecteurs simultanés.
Javier
6
@Javier Je pense que vous voulez dire lock(0L, Long.MAX_VALUE, true), non lock(0L, Long.MAX_VALUE, false). le dernier argument est boolean shared docs.oracle.com/javase/8/docs/api/java/nio/channels/…
john sullivan
60

N'utilisez pas les classes du java.iopackage, utilisez plutôt le java.niopackage. Ce dernier a une FileLockclasse. Vous pouvez appliquer un verrou à un fichier FileChannel.

 try {
        // Get a file channel for the file
        File file = new File("filename");
        FileChannel channel = new RandomAccessFile(file, "rw").getChannel();

        // Use the file channel to create a lock on the file.
        // This method blocks until it can retrieve the lock.
        FileLock lock = channel.lock();

        /*
           use channel.lock OR channel.tryLock();
        */

        // Try acquiring the lock without blocking. This method returns
        // null or throws an exception if the file is already locked.
        try {
            lock = channel.tryLock();
        } catch (OverlappingFileLockException e) {
            // File is already locked in this thread or virtual machine
        }

        // Release the lock - if it is not null!
        if( lock != null ) {
            lock.release();
        }

        // Close the file
        channel.close();
    } catch (Exception e) {
    }
ayengin
la source
btw, j'écris sur le fichier de verrouillage le pid actuel de cette astuce stackoverflow.com/a/35885/1422630 , donc après je peux le lire sur la nouvelle instance!
Aquarius Power
1
celui-ci avait l'air bien, mais cela ne fonctionne pas. J'obtiens l'exception OverlappingFileLockException à chaque fois, même si le fichier n'existait même pas
Gavriel
1
Un problème se produira si vous appelez tryLock après lock comme il est écrit dans l'exemple
Igor Vuković
17

Si vous pouvez utiliser Java NIO ( JDK 1.4 ou supérieur ), je pense que vous recherchezjava.nio.channels.FileChannel.lock()

FileChannel.lock ()

KC Baltz
la source
5
Peut être. Cela dépend de ce que OP entendait par «processus». "Les verrous de fichiers sont détenus au nom de l'ensemble de la machine virtuelle Java. Ils ne sont pas adaptés pour contrôler l'accès à un fichier par plusieurs threads au sein de la même machine virtuelle."
Stu Thompson
@Stu: Je sais que vous avez répondu à cette question il y a longtemps, mais j'espère que vous pourrez expliquer ce que vous voulez dire quand vous avez ditFile locks are held on behalf of the entire Java virtual machine. They are not suitable for controlling access to a file by multiple threads within the same virtual machine
Thang Pham
3
@Harry Il cite dans la documentation: download.oracle.com/javase/6/docs/api/java/nio/channels/ ... Cela signifie qu'il est invisible pour les threads mais affecte d'autres processus.
Artur Czajka le
@Harry: Pour ajouter encore plus à ces nécro-commentaires, imaginez que vous utilisez Java pour servir des sites Web avec Tomcat. Vous pouvez avoir beaucoup de threads, chacun traitant une requête à partir d'un navigateur Web. Cependant, ils contrôlent tous le même mécanisme de verrouillage des fichiers comme trop de cuisiniers dans une cuisine. Une demande peut se terminer au milieu d'une seconde, et soudainement votre fichier est «déverrouillé» alors que vous êtes encore au milieu de quelque chose, puis un autre processus comme un cronjob peut le verrouiller, et vous avez perdu votre verrouiller et votre demande ne peut pas se terminer ...
Darien
5

Ce n'est peut-être pas ce que vous recherchez, mais dans le but d'aborder un problème sous un autre angle ...

Ces deux processus Java peuvent-ils souhaiter accéder au même fichier dans la même application? Peut-être pouvez-vous simplement filtrer tous les accès au fichier via une seule méthode synchronisée (ou, mieux encore, en utilisant JSR-166 )? De cette façon, vous pouvez contrôler l'accès au fichier et peut-être même les demandes d'accès en file d'attente.

pkaeding
la source
3
Deux processus ne peuvent pas utiliser la synchronisation, seulement deux threads dans le même processus.
Marquis de Lorne
3

Utilisez un RandomAccessFile, récupérez son canal, puis appelez lock (). Le canal fourni par les flux d'entrée ou de sortie ne dispose pas de privilèges suffisants pour se verrouiller correctement. Assurez-vous d'appeler unlock () dans le bloc finally (la fermeture du fichier ne libère pas nécessairement le verrou).

Kevin Day
la source
peux-tu élaborer? Je veux dire, dans quelle mesure le verrou de RandomAccess File est-il meilleur ou plus sûr que streams one
Paralife
Lien vers l'exemple simple posté ci
Touko
Paralife - désolé pour le retard - vient de remarquer votre question. Les verrous des flux vont être des verrous de lecture (pour les flux d'entrée) et des verrous d'écriture exclusifs et complets (pour les flux de sortie). Mon expérience a été que les verrous de RAF permettent un contrôle plus fin (c'est-à-dire que vous pouvez verrouiller des parties d'un fichier).
Kevin Day
1

Vous trouverez ci-dessous un exemple de code pour verrouiller un fichier jusqu'à ce que son processus soit effectué par JVM.

 public static void main(String[] args) throws InterruptedException {
    File file = new File(FILE_FULL_PATH_NAME);
    RandomAccessFile in = null;
    try {
        in = new RandomAccessFile(file, "rw");
        FileLock lock = in.getChannel().lock();
        try {

            while (in.read() != -1) {
                System.out.println(in.readLine());
            }
        } finally {
            lock.release();
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        try {
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
Ajay Kumar
la source