Java kullanarak bir dosyayı nasıl kilitleyebilirim (mümkünse)


121

Dosya Okuyucu kullanarak dosya açan bir Java işlemim var. Başka bir (Java) işleminin bu dosyayı açmasını nasıl engelleyebilirim veya en azından ikinci işleme dosyanın zaten açıldığını nasıl bildirebilirim? Bu, dosya açıksa (bu benim sorunumu çözer) ikinci işlemin otomatik olarak bir istisna almasına neden olur mu yoksa onu ilk işlemde bir tür bayrak veya argümanla açıkça açmam gerekir mi?

Netleştirmek için:

Bir klasörü listeleyen ve listedeki her dosyayı işlemek için açan bir Java uygulamam var. Her dosyayı birbiri ardına işler. Her dosyanın işlenmesi, okunması ve içeriğine göre bazı hesaplamalar yapılmasından oluşur ve yaklaşık 2 dakika sürer. Aynı şeyi yapan ancak bunun yerine dosyaya yazan başka bir Java uygulamam da var. İstediğim şey, bu uygulamaları aynı anda çalıştırabilmek, böylece senaryo böyle devam ediyor. ReadApp klasörü listeler ve A, B, C dosyalarını bulur. A dosyasını açar ve okumaya başlar. WriteApp klasörü listeler ve A, B, C dosyalarını bulur, A dosyasını açar, açık olduğunu görür (istisnai olarak veya herhangi bir şekilde) ve B dosyasına gider ReadApp A dosyasını bitirir ve B'ye devam eder. açık ve C'ye devam ediyor WriteApp'ın ReadApp aynı dosyayı okurken veya tersini okurken yazın. Farklı süreçlerdir.


12
Süreçte (iki JVM) veya iş parçacığında (aynı JVM) 'işlem' mi demek istiyorsunuz ? Cevap üzerindeki etkisi çok önemlidir.
Stu Thompson

çözümü gösteren örnek kodu burada kontrol edin: stackoverflow.com/a/58871479/5154619
Davi Cavalcanti

Yanıtlar:


117

FileChannel.lock muhtemelen istediğiniz şeydir.

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

(Feragatname: Kod derlenmemiştir ve kesinlikle test edilmemiştir.)

FileLock için API dokümanındaki "platform bağımlılıkları" başlıklı bölüme dikkat edin .


22
Daha da önemlisi, JVM için kilitlemenin ve dosyanın tek bir JVM içinde ayrı iş parçacıkları tarafından erişim için kilitlenmesine uygun olmadığını anlayın.
Stu Thompson

11
Yazılabilir bir akışa (yani FileOutputStream) ihtiyacınız var.
Javier

@Javier Do you? Ben denemedim. API belgelerinde bunun bir gereklilik olduğunu söyleyen hiçbir şey atlamaz. FileOutputStreama için pek kullanışlı olmayacak Reader.
Tom Hawtin - tackline

18
Evet, denedim ve fırlattı NonWritableChannelException, çünkü lock()özel bir kilit almaya çalışıyor, ancak bu yazma erişimi gerektiriyor. Bir giriş akışınız varsa, lock(0L, Long.MAX_VALUE, false)hangisinin paylaşılan bir kilit alacağını ve yalnızca bir okuma erişimi gerektirdiğini kullanabilirsiniz. RandomAccessFileOkurken özel bir kilit istiyorsanız, okuma-yazma modunda açılmış bir modu da kullanabilirsiniz ... ancak bu eşzamanlı okuyucuları yasaklayacaktır.
Javier

6
@Javier Bence demek istediğini lock(0L, Long.MAX_VALUE, true)değil lock(0L, Long.MAX_VALUE, false). son argüman boolean shared docs.oracle.com/javase/8/docs/api/java/nio/channels/…
john sullivan

60

java.ioPaketteki sınıfları kullanmayın , bunun yerine java.niopaketi kullanın . İkincisinin bir FileLocksınıfı var. Bir 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, bu ipucu stackoverflow.com/a/35885/1422630'dan mevcut pid'i kilit dosyasına yazıyorum , böylece yeni örnekte okuyabildikten sonra!
Aquarius Power

1
bu iyi görünüyordu ama işe yaramıyor. Dosya mevcut olmasa bile her seferinde OverlappingFileLockException'ı alıyorum
Gavriel

1
Örnekte yazıldığı gibi kilitledikten sonra tryLock'u çağırırsanız sorun olur
Igor Vuković

17

Eğer kullanabiliyorsa Java NIO ( JDK 1.4 veya üzeri ), o zaman aradığınız düşünüyorumjava.nio.channels.FileChannel.lock()

FileChannel.lock ()


5
Olabilir. OP'nin 'süreç' ile ne anlama geldiğine bağlıdır. "Dosya kilitleri, Java sanal makinesinin tamamı adına tutulur. Aynı sanal makine içindeki birden fazla iş parçacığı tarafından bir dosyaya erişimi kontrol etmek için uygun değildirler."
Stu Thompson

@Stu: Bu soruyu uzun zaman önce yanıtladığını biliyorum, ama umarım derken ne demek istediğini açıklayabilirsinFile 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 Dokümanlardan alıntı yapıyor: download.oracle.com/javase/6/docs/api/java/nio/channels/… İş parçacıkları için görünmez olduğu ancak diğer süreçleri etkilediği anlamına gelir.
Artur Czajka

@Harry: Bu nekro yorumlara daha da fazlasını eklemek için, Tomcat ile web sitelerine hizmet vermek için Java kullandığınızı hayal edin. Her biri bir web tarayıcısından bir istek sunan çok sayıda ileti diziniz olabilir. Bununla birlikte, hepsi bir mutfaktaki çok fazla aşçı gibi aynı dosya kilitleme mekanizmasını kontrol eder. Bir istek, bir saniyenin ortasında bitebilir ve siz hala bir şeyin ortasındayken aniden dosyanızın "kilidi açılabilir" ve sonra bir cronjob gibi başka bir süreç onu kilitleyebilir ve sonra beklenmedik bir şekilde, kilitlenir ve isteğiniz tamamlanamaz ...
Darien


5

Aradığınız şey bu olmayabilir, ancak bir soruna başka bir açıdan gelmek için ....

Bu iki Java işlemi aynı dosyaya aynı uygulamada erişmek isteyebilir mi? Belki de dosyaya tüm erişimi tek ve senkronize bir yöntemle (veya daha da iyisi JSR-166 kullanarak ) filtreleyebilirsiniz. Bu şekilde, dosyaya erişimi kontrol edebilir ve hatta erişim isteklerini sıraya koyabilirsiniz.


3
İki işlem senkronizasyonu kullanamaz, yalnızca aynı işlemde iki iş parçacığı kullanılabilir.
Marquis of Lorne

3

Bir RandomAccessFile kullanın, kanalını alın ve ardından lock () çağırın. Giriş veya çıkış akışları tarafından sağlanan kanal, düzgün kilitlemek için yeterli ayrıcalığa sahip değil. Nihayet bloğunda unlock () işlevini çağırdığınızdan emin olun (dosyanın kapatılması, mutlaka kilidi serbest bırakmaz).


detaylandırır mısın? Demek istediğim, RandomAccess File tarafından sağlanan kilit, akışlardan daha iyi veya daha güvenli
Paralife

Aşağıda yayınlanan basit örneğe bağlantı
Touko

Paralife - gecikme için özür dilerim - sadece sorunuzu fark ettim. Akışlardan gelen kilitler, okuma kilitleri (giriş akışları için) ve özel, tam kanal yazma kilitleri (çıkış akışları için) olacaktır. Deneyimlerime göre, RAF'ın kilitleri daha hassas kontrole izin veriyor (yani bir dosyanın bölümlerini kilitleyebilirsiniz).
Kevin Day

1

Aşağıda, JVM tarafından işlem tamamlanana kadar bir dosyayı kilitlemek için örnek bir kod parçası bulunmaktadır.

 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();
        }
    }

}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.