Como posso bloquear um arquivo usando java (se possível)

121

Eu tenho um processo Java que abre um arquivo usando um FileReader. Como posso impedir que outro processo (Java) abra esse arquivo ou, pelo menos, notifique esse segundo processo que o arquivo já está aberto? Isso faz automaticamente o segundo processo obter uma exceção se o arquivo estiver aberto (o que resolve meu problema) ou eu tenho que abri-lo explicitamente no primeiro processo com algum tipo de sinalizador ou argumento?

Esclarecer:

Eu tenho um aplicativo Java que lista uma pasta e abre cada arquivo na listagem para processá-lo. Ele processa cada arquivo após o outro. O processamento de cada arquivo consiste em lê-lo e fazer alguns cálculos com base no conteúdo, e leva cerca de 2 minutos. Eu também tenho outro aplicativo Java que faz a mesma coisa, mas que grava no arquivo. O que eu quero é poder executar esses aplicativos ao mesmo tempo, para que o cenário seja assim. O ReadApp lista a pasta e localiza os arquivos A, B, C. Ele abre o arquivo A e inicia a leitura. O WriteApp lista a pasta e localiza os arquivos A, B, C. Ele abre o arquivo A, vê que está aberto (por uma exceção ou de qualquer maneira) e vai para o arquivo B. ReadApp termina o arquivo A e continua para B. Ele vê que está está aberto e continua até C. É crucial que o WriteApp não Não escreva enquanto o ReadApp está lendo o mesmo arquivo ou vice-versa. São processos diferentes.

Paralife
fonte
12
Você quer dizer 'processo' como no processo (duas JVMs) ou encadeamento (a mesma JVM). O impacto na resposta é primordial.
Stu Thompson
verifique amostra de solução de código mostrando aqui: stackoverflow.com/a/58871479/5154619
Davi Cavalcanti

Respostas:

117

FileChannel.lock é provavelmente o que você deseja.

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

(Isenção de responsabilidade: código não compilado e certamente não testado.)

Observe a seção intitulada "dependências da plataforma" no documento da API para FileLock .

Tom Hawtin - linha de orientação
fonte
22
Mais importante, entenda que o bloqueio para a JVM e não é adequado para bloquear o arquivo para acesso por encadeamentos individuais em uma única JVM.
Stu Thompson
11
Você precisa de um fluxo gravável (ou seja FileOutputStream).
Javier
@Javier você? Eu não tentei. Nada sai dos documentos da API dizendo que isso é um requisito. FileOutputStreamnão vai ser muito útil para um Reader.
Tom Hawtin - tackline
18
Sim, eu tentei e lança NonWritableChannelException, porque lock()tenta adquirir um bloqueio exclusivo, mas isso requer acesso de gravação. Se você tiver um fluxo de entrada , poderá usar o lock(0L, Long.MAX_VALUE, false)que adquire um bloqueio compartilhado e requer apenas um acesso de leitura. Você também pode usar um modo RandomAccessFileaberto de leitura / gravação se desejar um bloqueio exclusivo durante a leitura ... mas isso proibiria os leitores simultâneos.
Javier
6
@ Jorge Eu acho que você quer dizer lock(0L, Long.MAX_VALUE, true), não lock(0L, Long.MAX_VALUE, false). o último argumento existe boolean shared docs.oracle.com/javase/8/docs/api/java/nio/channels/…
john sullivan
60

Não use as classes no java.iopacote, use o java.niopacote. Este último tem uma FileLockclasse. Você pode aplicar um bloqueio a 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
fonte
btw, estou escrevendo no arquivo de bloqueio o pid atual desta dica stackoverflow.com/a/35885/1422630 , para que depois eu possa lê-lo na nova instância!
Poder de Aquário
1
este parecia bom, mas não funciona. Tenho a OverlappingFileLockException cada vez, mesmo quando o arquivo nem sequer exsist
Gavriel
1
O problema acontecerá se você chamar o tryLock após o bloqueio, como está escrito no exemplo
Igor Vuković 22/03
17

Se você pode usar o Java NIO ( JDK 1.4 ou superior ), acho que está procurandojava.nio.channels.FileChannel.lock()

FileChannel.lock ()

KC Baltz
fonte
5
Talvez. Depende do que OP significa "processo". "Os bloqueios de arquivo são mantidos em nome de toda a máquina virtual Java. Eles não são adequados para controlar o acesso a um arquivo por vários encadeamentos na mesma máquina virtual."
Stu Thompson
@Stu: Eu sei que você já respondeu a esta pergunta há muito tempo, mas eu espero que você possa elaborar o que você quer dizer quando disseFile 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 Ele está citando os documentos: download.oracle.com/javase/6/docs/api/java/nio/channels/… Isso significa que é invisível para threads, mas afeta outros processos.
Artur Czajka
@ Harry: Para adicionar ainda mais a esses necro-comentários, imagine que você esteja usando Java para servir sites com o Tomcat. Você pode ter muitos threads, cada um atendendo a uma solicitação de um navegador da web. No entanto, todos eles controlam o mesmo mecanismo de bloqueio de arquivos, como muitos cozinheiros em uma cozinha. Uma solicitação pode terminar no meio de uma segunda e, de repente, seu arquivo é "desbloqueado" enquanto você ainda está no meio de alguma coisa. Em seguida, algum outro processo como um cronjob pode bloqueá-lo e você perde inesperadamente seu bloqueio e o seu pedido não pode terminar ...
Darien
5

Pode não ser o que você está procurando, mas no interesse de encontrar um problema de outro ângulo ....

Esses dois processos Java podem querer acessar o mesmo arquivo no mesmo aplicativo? Talvez você possa filtrar todo o acesso ao arquivo através de um único método sincronizado (ou, melhor ainda, usando o JSR-166 )? Dessa forma, você pode controlar o acesso ao arquivo e, talvez, até solicitações de acesso na fila.

pkaeding
fonte
3
Dois processos não podem usar a sincronização, apenas dois threads no mesmo processo.
Marquês de Lorne # /
3

Use um RandomAccessFile, obtenha o canal e chame lock (). O canal fornecido pelos fluxos de entrada ou saída não possui privilégios suficientes para bloquear corretamente. Certifique-se de chamar unlock () no bloco final (fechar o arquivo não necessariamente libera o bloqueio).

Kevin Day
fonte
Você pode elaborar? Quer dizer, até que ponto é o bloqueio por File RandomAccess melhor ou mais seguro do que fluxos um
Paralife
Link para o exemplo simples postado abaixo
Touko
Paralife - desculpe pelo atraso - acabou de notar sua pergunta. Os bloqueios dos fluxos serão bloqueios de leitura (para fluxos de entrada) e bloqueios exclusivos de gravação de canal completo (para fluxos de saída). Minha experiência foi que os bloqueios do RAF permitem um controle mais refinado (ou seja, você pode bloquear partes de um arquivo).
24572 Kevin Day
1

Abaixo está um código de trecho de amostra para bloquear um arquivo até que seu processo seja concluído pela 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
fonte