Hvordan låse en fil i Java

1. Oversikt

Når vi leser eller skriver filer, må vi sørge for at riktige fillåsemekanismer er på plass. Dette sikrer dataintegritet i samtidige I / O-baserte applikasjoner.

I denne opplæringen vil vi se på ulike tilnærminger for å oppnå dette ved hjelp av Java NIO-biblioteket.

2. Introduksjon til fillåser

Generelt er det to typer låser:

    • Eksklusive låser - også kjent som skrivelåser
    • Delt låser - også referert til som lest låser

Enkelt sagt forhindrer en eksklusiv lås alle andre operasjoner - inkludert lesing - mens en skriveoperasjon er fullført.

I kontrast tillater en delt lås mer enn én prosess å lese samtidig. Poenget med en leselås er å forhindre anskaffelse av en skrivelås ved en annen prosess. Vanligvis bør en fil i en konsistent tilstand leses av alle prosesser.

I neste avsnitt vil vi se hvordan Java håndterer disse typer låser.

3. Fillåser i Java

Java NIO-biblioteket muliggjør låsing av filer på operativsystemnivå. De låse() og tryLock () metoder for a FileChannel er for det formålet.

Vi kan lage en FileChannel gjennom enten a FileInputStream, a FileOutputStream, eller a RandomAccessFile. Alle tre har en getChannel () metode som returnerer a FileChannel.

Alternativt kan vi lage en FileChannel direkte via det statiske åpen metode:

prøv (FileChannel channel = FileChannel.open (sti, openOptions)) {// skriv til kanalen}

Deretter vil vi se på forskjellige alternativer for å få eksklusive og delte låser i Java. For å lære mer om filkanaler, sjekk ut Guide to Java FileChannel tutorial.

4. Eksklusive låser

Som vi allerede har lært, mens du skriver til en fil, vi kan forhindre at andre prosesser leser eller skriver til den ved å bruke en eksklusiv lås.

Vi får eksklusive låser ved å ringe låse() eller tryLock ()FileChannel klasse. Vi kan også bruke deres overbelastede metoder:

  • lås (lang posisjon, lang størrelse, boolsk delt)
  • tryLock (lang posisjon, lang størrelse, boolsk delt)

I disse tilfellene, delt parameteren må settes til falsk.

For å få en eksklusiv lås, må vi bruke en skrivbar FileChannel. Vi kan skape det gjennom getChannel () metoder for a FileOutputStream eller a RandomAccessFile. Alternativt, som tidligere nevnt, kan vi bruke det statiske åpen metoden for FileChannel klasse. Alt vi trenger er å sette det andre argumentet til StandardOpenOption.APPEND:

prøv (FileChannel channel = FileChannel.open (sti, StandardOpenOption.APPEND)) {// skriv til kanal}

4.1. Eksklusive låser ved bruk av en FileOutputStream

EN FileChannel opprettet fra en FileOutputStream er skrivbar. Vi kan derfor skaffe oss en eksklusiv lås:

prøv (FileOutputStream fileOutputStream = ny FileOutputStream ("/ tmp / testfile.txt"); FileChannel channel = fileOutputStream.getChannel (); FileLock lock = channel.lock ()) {// skriv til kanalen}

Her, channel.lock () vil enten blokkere til den får en lås, eller det vil kaste et unntak. For eksempel, hvis den angitte regionen allerede er låst, vil en OverlappingFileLockException blir kastet. Se Javadoc for en komplett liste over mulige unntak.

Vi kan også utføre en ikke-blokkerende lås ved hjelp av channel.tryLock (). Hvis det ikke lykkes å få en lås fordi et annet program har en overlappende en, går den tilbake null. Hvis det ikke gjør det av annen grunn, kastes et passende unntak.

4.2. Eksklusive låser ved bruk av en RandomAccessFile

Med en RandomAccessFile, må vi sette flagg på den andre parameteren til konstruktøren.

Her skal vi åpne filen med lese- og skrivetillatelser:

prøv (RandomAccessFile file = new RandomAccessFile ("/ tmp / testfile.txt", "rw"); FileChannel channel = file.getChannel (); FileLock lock = channel.lock ()) {// skriv til kanalen} 

Hvis vi åpner filen i skrivebeskyttet modus og prøver å skrive til kanalen, vil den kaste a NonWritableChannelException.

4.3. Eksklusive låser krever en skrivbar FileChannel

Som nevnt tidligere trenger eksklusive låser en skrivbar kanal. Derfor kan vi ikke få en eksklusiv lås gjennom a FileChannel opprettet fra en FileInputStream:

Sti bane = Files.createTempFile ("foo", "txt"); Logger log = LoggerFactory.getLogger (this.getClass ()); prøv (FileInputStream fis = ny FileInputStream (path.toFile ()); FileLock lock = fis.getChannel (). lock ()) {// unreachable code} catch (NonWritableChannelException e) {// handle unntak}

I eksemplet ovenfor er låse() metoden vil kaste en NonWritableChannelException. Dette er faktisk fordi vi påkaller getChannel på en FileInputStream, som skaper en skrivebeskyttet kanal.

Dette eksemplet er bare for å demonstrere at vi ikke kan skrive til en ikke-skrivbar kanal. I et virkelig scenario vil vi ikke fange og omgjøre unntaket.

5. Delt lås

Husk at også delte låser kalles lese låser. Derfor, for å få en leselås, må vi bruke en lesbar FileChannel.

Slik en FileChannel kan fås ved å ringe getChannel () metode på en FileInputStream eller a RandomAccessFile. Igjen, et annet alternativ er å bruke det statiske åpen metoden for FileChannel klasse. I så fall setter vi det andre argumentet til StandardOpenOption.READ:

prøv (FileChannel channel = FileChannel.open (sti, StandardOpenOption.READ); FileLock lock = channel.lock (0, Long.MAX_VALUE, true)) {// lest fra kanalen}

En ting å merke seg her er at vi valgte å låse hele filen ved å ringe lock (0, Long.MAX_VALUE, true). Vi kunne også ha låst bare en bestemt region av filen ved å endre de to første parametrene til forskjellige verdier. Den tredje parameteren må settes til ekte i tilfelle en delt lås.

For å gjøre det enkelt vil vi låse hele filen i alle eksemplene nedenfor, men husk at vi alltid kan låse en bestemt region i en fil.

5.1. Delt lås ved hjelp av en FileInputStream

EN FileChannel hentet fra en FileInputStream er lesbar. Vi kan derfor få en delt lås:

prøv (FileInputStream fileInputStream = ny FileInputStream ("/ tmp / testfile.txt"); FileChannel channel = fileInputStream.getChannel (); FileLock lock = channel.lock (0, Long.MAX_VALUE, true)) {// lese fra kanalen }

I utdraget over kalles til låse() på kanalen vil lykkes. Det er fordi en delt lås bare krever at kanalen er lesbar. Det er tilfelle her siden vi opprettet det fra en FileInputStream.

5.2. Delt lås ved hjelp av en RandomAccessFile

Denne gangen kan vi åpne filen med bare lese tillatelser:

prøv (RandomAccessFile file = new RandomAccessFile ("/ tmp / testfile.txt", "r"); FileChannel channel = file.getChannel (); FileLock lock = channel.lock (0, Long.MAX_VALUE, true)) {// lese fra kanalen}

I dette eksemplet opprettet vi en RandomAccessFile med lesetillatelser. Vi kan lage en lesbar kanal fra den og dermed opprette en delt lås.

5.3. Delt lås krever en lesbar FileChannel

Av den grunn kan vi ikke anskaffe en delt lås gjennom en FileChannel opprettet fra en FileOutputStream:

Sti bane = Files.createTempFile ("foo", "txt"); prøv (FileOutputStream fis = ny FileOutputStream (path.toFile ()); FileLock lock = fis.getChannel (). lock (0, Long.MAX_VALUE, true)) {// unreachable code} catch (NonWritableChannelException e) {// handle unntak} 

I dette eksemplet kalles til låse() prøver å få en delt lås på en kanal opprettet fra en FileOutputStream. En slik kanal er skrivebeskyttet. Det oppfyller ikke behovet for at kanalen må være lesbar. Dette vil utløse en NonWritableChannelException.

Igjen, dette utdraget er bare for å demonstrere at vi ikke kan lese fra en ikke-lesbar kanal.

6. Ting du bør vurdere

I praksis er det vanskelig å bruke fillåser; låsemekanismene er ikke bærbare. Vi må lage låselogikken vår med tanke på dette.

I POSIX-systemer er låser rådgivende. Ulike prosesser som leser eller skriver til en gitt fil, må være enige om en låseprotokoll. Dette vil sikre filens integritet. Operativsystemet i seg selv håndhever ingen låsning.

På Windows vil låser være eksklusive med mindre deling er tillatt. Å diskutere fordeler eller ulemper ved OS-spesifikke mekanismer ligger utenfor omfanget av denne artikkelen. Likevel er det viktig å kjenne til disse nyansene når du implementerer en låsemekanisme.

7. Konklusjon

I denne opplæringen har vi gjennomgått flere forskjellige alternativer for å skaffe fillåser i Java.

Først startet vi med å forstå de to viktigste låsemekanismene og hvordan Java NIO-biblioteket letter låsing av filer. Så gikk vi gjennom en rekke enkle eksempler som viser at vi kan få eksklusive og delte låser i applikasjonene våre. Vi tok også en titt på typene av typiske unntak vi kan støte på når vi arbeider med fillåser.

Som alltid er kildekoden for eksemplene tilgjengelig på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found