13 votes

Comment fonctionne FileLock ?

J'ai essayé d'utiliser FileLock pour obtenir un accès exclusif à un fichier afin de :

  • le supprimer
  • le renommer
  • lui écrire

Parce que sous Windows (au moins), il semble que vous ne pouvez pas supprimer, renommer ou écrire dans un fichier qui est déjà utilisé. Le code que j'ai écrit ressemble à quelque chose comme ceci :

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

public abstract class LockedFileOperation {

    public void execute(File file) throws IOException {

        if (!file.exists()) {
            throw new FileNotFoundException(file.getAbsolutePath());
        }

        FileChannel channel = new RandomAccessFile(file, "rw").getChannel();

        try {
            // Get an exclusive lock on the whole file
            FileLock lock = channel.lock();

            try {
                doWithLockedFile(file);
            } finally {
                lock.release();
            }
        } finally {
            channel.close();
        }
    }

    public abstract void doWithLockedFile(File file) throws IOException;
}

Voici quelques tests unitaires qui démontrent le problème. Vous devez avoir Apache commons-io dans votre classpath pour exécuter le 3ème test.

import java.io.File;
import java.io.IOException;

import junit.framework.TestCase;

public class LockedFileOperationTest extends TestCase {

    private File testFile;

    @Override
    protected void setUp() throws Exception {

        String tmpDir = System.getProperty("java.io.tmpdir");
        testFile = new File(tmpDir, "test.tmp");

        if (!testFile.exists() && !testFile.createNewFile()) {
            throw new IOException("Failed to create test file: " + testFile);
        }
    }

    public void testRename() throws IOException {
        new LockedFileOperation() {

            @Override
            public void doWithLockedFile(File file) throws IOException {
                if (!file.renameTo(new File("C:/Temp/foo"))) {
                    fail();
                }
            }
        }.execute(testFile);
    }

    public void testDelete() throws IOException {
        new LockedFileOperation() {

            @Override
            public void doWithLockedFile(File file) throws IOException {
                if (!file.delete()) {
                    fail();
                }
            }
        }.execute(testFile);
    }

    public void testWrite() throws IOException {
        new LockedFileOperation() {

            @Override
            public void doWithLockedFile(File file) throws IOException {
                org.apache.commons.io.FileUtils.writeStringToFile(file, "file content");
            }
        }.execute(testFile);
    }
}

Aucun des tests ne passe. Les deux premiers échouent, et le dernier lève cette exception :

java.io.IOException: The process cannot access the file because another process has locked a portion of the file
    at java.io.FileOutputStream.writeBytes(Native Method)
    at java.io.FileOutputStream.write(FileOutputStream.java:247)
    at org.apache.commons.io.IOUtils.write(IOUtils.java:784)
    at org.apache.commons.io.IOUtils.write(IOUtils.java:808)
    at org.apache.commons.io.FileUtils.writeStringToFile(FileUtils.java:1251)
    at org.apache.commons.io.FileUtils.writeStringToFile(FileUtils.java:1265)

Il semble que le lock() place un verrou sur le fichier, ce qui m'empêche ensuite de le renommer, de l'effacer ou de l'écrire. J'ai supposé que le verrouillage du fichier me donnerait un accès exclusif au fichier, de sorte que je pourrais ensuite le renommer/supprimer/écrire sans me soucier de savoir si un autre processus y accède également.

Soit je comprends mal FileLock ou ce n'est pas une solution appropriée à mon problème.

16voto

Kevin Brock Points 4695

Le message concernant un autre processus signifie simplement qu'un processus de votre système a le fichier ouvert. Il ne vérifie pas si ce processus est le même que celui qui tente de supprimer/renommer le fichier. Dans ce cas, le fichier est ouvert par le même programme. Vous l'avez ouvert pour obtenir le verrou. Le verrou n'a ici que peu ou pas de valeur, surtout si vous faites cela pour des opérations de suppression ou de renommage.

Pour faire ce que vous voulez, vous devez verrouiller l'entrée du répertoire. Ceci n'est pas disponible en Java et peut ne pas l'être en Windows. Ces opérations (suppression et insertion) sont atomiques. Cela signifie que le système d'exploitation se charge de verrouiller le répertoire et les autres structures du système de fichiers pour vous. Si un autre processus (ou le vôtre) a le fichier ouvert, ces opérations échoueront. Si vous essayez de verrouiller le fichier exclusivement (entrée du répertoire) et qu'un autre processus (ou le vôtre) a le fichier ouvert, le verrouillage échouera. Il n'y a pas de différence, mais tenter d'effectuer le verrouillage ne fait que compliquer, et dans ce cas, rend l'opération impossible (c'est-à-dire que les fichiers sont toujours ouverts avant que vous ne tentiez d'effectuer l'opération).

Maintenant, écrire dans le fichier est une opération de verrouillage valide. Verrouillez le fichier ou la partie du fichier sur laquelle vous voulez écrire et cela fonctionnera. Sous Windows, ce mécanisme de verrouillage est obligatoire, de sorte qu'un autre descripteur de fichier/ouvert ne pourra pas écrire dans une partie qui est sous le verrou.

EDIT

Selon la JavaDoc sur FileChannel.lock c'est la même chose que d'appeler FileChannel.lock(0L, Long.MAXVALUE, false) . Il s'agit d'un verrouillage exclusif sur une région du premier au dernier octet.

Deuxièmement, selon la JavaDoc sur FileLock

Le fait qu'un verrou empêche ou non un autre programme d'accéder au contenu de la région verrouillée dépend du système et n'est donc pas spécifié. Les fonctions natives de verrouillage de fichiers de certains systèmes sont simplement consultatives, ce qui signifie que les programmes doivent coopérer en respectant un protocole de verrouillage connu afin de garantir l'intégrité des données. Sur d'autres systèmes, les verrous de fichiers natifs sont obligatoires, ce qui signifie que si un programme verrouille une région d'un fichier, les autres programmes ne peuvent pas accéder à cette région d'une manière qui violerait le verrou. Sur d'autres systèmes encore, le caractère consultatif ou obligatoire des verrous de fichiers natifs est configurable pour chaque fichier. Pour assurer un comportement cohérent et correct sur toutes les plateformes, il est fortement recommandé d'utiliser les verrous fournis par cette API comme s'il s'agissait de verrous consultatifs. .

EDIT

Pour le testWrite méthode. La JavaDoc sur la méthode statique commons I/O est peu détaillée mais dit "Ecrit une chaîne de caractères dans un fichier en créant le fichier s'il n'existe pas " et étant donné que cette méthode prend une valeur de File au lieu d'un flux ouvert, il ouvre probablement le fichier en interne. Il est probable qu'il n'ouvre pas le fichier avec un accès partagé et qu'il l'ouvre également pour un accès par appendice. Cela signifie que l'ouverture et le verrouillage existants (votre ouverture pour obtenir le canal à partir duquel obtenir le verrouillage) bloquent cette utilisation. Pour en savoir plus, vous devriez obtenir les sources de cette méthode et examiner ce qu'elle fait.

EDIT

Désolé, je me suis trompé. J'ai vérifié l'API de Windows et le verrouillage des fichiers est obligatoire sous Windows. C'est pourquoi l'écriture échoue. La première ouverture (votre new RandomAccessFile ) et lock a le fichier verrouillé. L'ouverture pour écrire la chaîne réussit mais l'écriture échoue parce qu'une autre ouverture (descripteur de fichier) a la totalité du fichier sous un verrou exclusif obligatoire - c'est-à-dire qu'aucun autre descripteur de fichier ne peut écrire dans le fichier jusqu'à ce que le verrou soit libéré.

Notez que le verrouillage est associé au descripteur de fichier PAS le processus ou le fil.

1voto

Eugene Kuleshov Points 17757

La serrure que vous cherchez est verrouillage d'une région à l'intérieur d'un fichier Par conséquent, tant que la région est verrouillée, vous ne pouvez pas supprimer ou renommer le fichier.

Vous pouvez consulter le Transaction commune projet.

1voto

dogbane Points 85749

El delete y rename sont effectuées par le système d'exploitation et sont atomiques (sur la plupart des systèmes d'exploitation), donc aucun verrouillage n'est nécessaire.

Pour écrire une chaîne dans un fichier, il serait plus simple d'écrire d'abord dans un fichier temporaire (par exemple foo.tmp), puis de le renommer une fois qu'il est prêt.

0voto

EJP Points 113412

Les verrous de fichiers Java sont spécifiés uniquement pour se protéger contre d'autres verrous, et rien d'autre. La façon dont ils se comportent sur des plates-formes spécifiques, c'est-à-dire toute sémantique supplémentaire, est spécifique à la plate-forme.

0voto

itro Points 1120

Vous devez libérer le fichier avec la méthode release() avant d'effectuer toute action comme renommer, supprimer ou .....

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X