Come posso bloccare un file usando java (se possibile)


121

Ho un processo Java che apre un file utilizzando un FileReader. Come posso impedire a un altro processo (Java) di aprire questo file o almeno notificare al secondo processo che il file è già aperto? Questo fa sì che il secondo processo ottenga automaticamente un'eccezione se il file è aperto (il che risolve il mio problema) o devo aprirlo esplicitamente nel primo processo con una sorta di flag o argomento?

Chiarire:

Ho un'app Java che elenca una cartella e apre ogni file nell'elenco per elaborarlo. Elabora ogni file dopo l'altro. L'elaborazione di ogni file consiste nel leggerlo e fare alcuni calcoli in base al contenuto e richiede circa 2 minuti. Ho anche un'altra app Java che fa la stessa cosa ma invece scrive sul file. Quello che voglio è essere in grado di eseguire queste app allo stesso tempo, quindi lo scenario va così. ReadApp elenca la cartella e trova i file A, B, C. Apre il file A e avvia la lettura. WriteApp elenca la cartella e trova i file A, B, C.Apre il file A, vede che è aperto (per un'eccezione o in qualsiasi modo) e va al file B. ReadApp termina il file A e continua su B.Vede che è aperto e continua a C. È fondamentale che WriteApp non lo faccia t scrivere mentre ReadApp sta leggendo lo stesso file o viceversa. Sono processi diversi.


12
Intendi "processo" come in processo (due JVM) o thread (stessa JVM). L'impatto sulla risposta è fondamentale.
Stu Thompson,

controlla il codice di esempio che mostra la soluzione qui: stackoverflow.com/a/58871479/5154619
Davi Cavalcanti

Risposte:


117

FileChannel.lock è probabilmente quello che vuoi.

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

(Disclaimer: codice non compilato e certamente non testato.)

Nota la sezione intitolata "dipendenze dalla piattaforma" nel documento API per FileLock .


22
Ancora più importante, capire che il blocco di per la JVM e non adatto per il blocco del file per l'accesso da parte di singoli thread all'interno di una singola JVM.
Stu Thompson

11
Hai bisogno di un flusso scrivibile (cioè FileOutputStream).
Javier

@Javier Do you? Non ho provato Nulla salta fuori dai documenti API dicendo che è un requisito. FileOutputStreamnon sarà molto utile per un file Reader.
Tom Hawtin - tackline

18
Sì, l'ho provato e genera NonWritableChannelException, perché lock()tenta di acquisire un blocco esclusivo, ma ciò richiede l'accesso in scrittura. Se si dispone di un flusso di input , è possibile utilizzare lock(0L, Long.MAX_VALUE, false)che acquisisce un blocco condiviso e richiede solo un accesso in lettura. Puoi anche utilizzare un RandomAccessFileaperto in modalità di lettura-scrittura se desideri un blocco esclusivo durante la lettura ... ma ciò impedirebbe lettori simultanei.
Javier

6
@ Javier, penso che tu voglia dire lock(0L, Long.MAX_VALUE, true), no lock(0L, Long.MAX_VALUE, false). l'ultimo argomento è boolean shared docs.oracle.com/javase/8/docs/api/java/nio/channels/…
john sullivan

60

Non usare le classi nel java.iopacchetto, usa invece il java.niopacchetto. Quest'ultimo ha una FileLockclasse. Puoi applicare un blocco a un file 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) {
    }

btw, sto scrivendo sul file di blocco il pid corrente da questo suggerimento stackoverflow.com/a/35885/1422630 , quindi dopo posso leggerlo sulla nuova istanza!
Aquarius Power

1
questo sembrava buono, ma non funziona. Ottengo l'eccezione OverlappingFileLockException ogni volta, anche quando il file non esisteva nemmeno
Gavriel

1
Il problema si verificherà se chiami tryLock dopo il blocco come è scritto nell'esempio
Igor Vuković

17

Se puoi usare Java NIO ( JDK 1.4 o superiore ), penso che tu stia cercandojava.nio.channels.FileChannel.lock()

FileChannel.lock ()


5
Può essere. Dipende da cosa intendeva OP per "processo". "I blocchi dei file vengono mantenuti per conto dell'intera macchina virtuale Java. Non sono adatti per controllare l'accesso a un file da parte di più thread all'interno della stessa macchina virtuale."
Stu Thompson,

@Stu: So che hai risposto a questa domanda molto tempo fa, ma spero che tu possa elaborare cosa intendi quando hai dettoFile 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 He sta citando dalla documentazione: download.oracle.com/javase/6/docs/api/java/nio/channels/… Significa che è invisibile ai thread ma influisce su altri processi.
Artur Czajka

@Harry: Per aggiungere ancora di più a questi commenti necrologici, immagina di utilizzare Java per servire siti Web con Tomcat. Potresti avere molti thread, ognuno dei quali serve una richiesta da un browser web. Tuttavia, controllano tutti lo stesso meccanismo di blocco dei file come troppi cuochi in una cucina. Una richiesta potrebbe terminare nel mezzo di una seconda e improvvisamente il tuo file è stato "sbloccato" mentre eri ancora nel mezzo di qualcosa, e poi qualche altro processo come un cronjob potrebbe bloccarlo, e poi hai perso inaspettatamente il tuo bloccare e la tua richiesta non può finire ...
Darien


5

Questo potrebbe non essere quello che stai cercando, ma nell'interesse di affrontare un problema da un'altra angolazione ...

Questi due processi Java che potrebbero voler accedere allo stesso file nella stessa applicazione? Forse puoi semplicemente filtrare tutti gli accessi al file attraverso un unico metodo sincronizzato (o, ancora meglio, usando JSR-166 )? In questo modo, puoi controllare l'accesso al file e forse anche accodare le richieste di accesso.


3
Due processi non possono utilizzare la sincronizzazione, solo due thread nello stesso processo.
Marchese di Lorne

3

Usa un RandomAccessFile, ottieni il suo canale, quindi chiama lock (). Il canale fornito dai flussi di input o output non dispone di privilegi sufficienti per bloccarsi correttamente. Assicurati di chiamare unlock () nel blocco finalmente (la chiusura del file non rilascia necessariamente il blocco).


Puoi elaborare? Voglio dire, fino a che punto è il blocco di RandomAccess File migliore o più sicuro dello streaming uno
Paralife

Link al semplice esempio pubblicato di seguito
Touko

Paralife - scusa per il ritardo - ha appena notato la tua domanda. I blocchi dai flussi saranno blocchi di lettura (per flussi di input) e blocchi di scrittura esclusivi e completi del canale (per flussi di output). La mia esperienza è stata che i blocchi di RAF consentono un controllo più dettagliato (ovvero è possibile bloccare parti di un file).
Kevin Day,

1

Di seguito è riportato un frammento di codice di esempio per bloccare un file finché il suo processo non viene eseguito da 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();
        }
    }

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.