Bagaimana cara mengunci file menggunakan java (jika memungkinkan)

121

Saya memiliki proses Java yang membuka file menggunakan FileReader. Bagaimana cara mencegah proses lain (Java) membuka file ini, atau setidaknya memberi tahu proses kedua bahwa file sudah dibuka? Apakah ini secara otomatis membuat proses kedua mendapatkan pengecualian jika file terbuka (yang memecahkan masalah saya) atau apakah saya harus membukanya secara eksplisit dalam proses pertama dengan semacam bendera atau argumen?

Untuk memperjelas:

Saya memiliki aplikasi Java yang mencantumkan folder dan membuka setiap file dalam daftar untuk memprosesnya. Ini memproses setiap file setelah yang lain. Pemrosesan setiap file terdiri dari membacanya dan melakukan beberapa perhitungan berdasarkan isinya dan memakan waktu sekitar 2 menit. Saya juga memiliki aplikasi Java lain yang melakukan hal yang sama tetapi menulis di file. Yang saya inginkan adalah dapat menjalankan aplikasi ini pada saat yang sama sehingga skenarionya berjalan seperti ini. ReadApp mencantumkan folder dan menemukan file A, B, C. Ini membuka file A dan mulai membaca. WriteApp mencantumkan folder dan menemukan file A, B, C. Ini membuka file A, melihat itu terbuka (dengan pengecualian atau cara apa pun) dan pergi ke file B. ReadApp menyelesaikan file A dan melanjutkan ke B. Ia melihat bahwa itu terbuka dan terus ke C. Sangat penting bahwa WriteApp tidak t menulis saat ReadApp membaca file yang sama atau sebaliknya. Mereka adalah proses yang berbeda.

Paralife
sumber
12
Apakah maksud Anda 'proses' seperti dalam proses (dua JVM) atau utas (JVM yang sama). Dampak pada jawabannya adalah yang terpenting.
Stu Thompson
periksa kode contoh yang menunjukkan solusi di sini: stackoverflow.com/a/58871479/5154619
Davi Cavalcanti

Jawaban:

117

FileChannel.lock mungkin yang Anda inginkan.

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

(Penafian: Kode tidak dikompilasi dan tentu saja tidak diuji.)

Perhatikan bagian berjudul "ketergantungan platform" di dokumen API untuk FileLock .

Tom Hawtin - taktik
sumber
22
Lebih penting lagi, memahami bahwa kunci untuk JVM, dan tidak cocok untuk mengunci file untuk akses oleh utas individu dalam JVM tunggal.
Stu Thompson
11
Anda membutuhkan aliran yang dapat ditulis (yaitu FileOutputStream).
Javier
@Javier Apakah Anda? Saya belum mencoba. Tidak ada yang keluar dari dokumen API yang mengatakan bahwa itu adalah persyaratan. FileOutputStreamtidak akan banyak berguna untuk a Reader.
Tom Hawtin - tackline
18
Ya, saya mencobanya dan itu terlempar NonWritableChannelException, karena lock()mencoba mendapatkan kunci eksklusif, tetapi itu memerlukan akses tulis. Jika Anda memiliki aliran input , Anda dapat menggunakannya lock(0L, Long.MAX_VALUE, false)yang memperoleh kunci bersama dan hanya memerlukan akses baca. Anda juga dapat menggunakan RandomAccessFilebuka dalam mode baca-tulis jika Anda menginginkan kunci eksklusif saat membaca ... tetapi itu akan melarang pembaca secara bersamaan.
Javier
6
@ Javier Saya pikir Anda bermaksud mengatakan lock(0L, Long.MAX_VALUE, true), tidak lock(0L, Long.MAX_VALUE, false). argumen terakhir ada boolean shared docs.oracle.com/javase/8/docs/api/java/nio/channels/…
john sullivan
60

Jangan gunakan kelas dalam java.iopaket, sebagai gantinya gunakan java.niopaket. Yang terakhir memiliki FileLockkelas. Anda dapat menerapkan kunci ke 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
sumber
btw, saya menulis di file kunci pid saat ini dari tip ini stackoverflow.com/a/35885/1422630 , jadi setelah saya bisa membacanya di contoh baru!
Aquarius Power
1
yang ini terlihat bagus, tapi tidak berhasil. Saya mendapatkan OverlappingFileLockException setiap saat, bahkan ketika file tersebut bahkan tidak ada
Gavriel
1
Masalah akan terjadi jika Anda memanggil tryLock demi kunci seperti yang tertulis dalam contoh
Igor Vuković
17

Jika Anda dapat menggunakan Java NIO ( JDK 1.4 atau lebih tinggi ), maka saya rasa Anda sedang mencarijava.nio.channels.FileChannel.lock()

FileChannel.lock ()

KC Baltz
sumber
5
Mungkin. Tergantung apa yang dimaksud OP dengan 'proses'. "Kunci file ditahan atas nama seluruh mesin virtual Java. Kunci tersebut tidak cocok untuk mengontrol akses ke file dengan beberapa utas dalam mesin virtual yang sama."
Stu Thompson
@Stu: Saya tahu Anda telah menjawab pertanyaan ini sejak lama, tetapi saya harap Anda dapat menjelaskan apa yang Anda maksud saat mengatakanFile 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 Dia mengutip dari dokumen: download.oracle.com/javase/6/docs/api/java/nio/channels/… Itu berarti tidak terlihat oleh utas tetapi memengaruhi proses lain.
Artur Czajka
@Harry: Untuk menambahkan lebih banyak lagi ke komentar necro ini, bayangkan Anda menggunakan Java untuk melayani situs web dengan Tomcat. Anda mungkin memiliki banyak utas, masing-masing melayani satu permintaan dari browser web. Namun, mereka semua mengontrol mekanisme penguncian file yang sama seperti terlalu banyak juru masak di dapur. Satu permintaan mungkin selesai di tengah-tengah permintaan kedua, dan tiba-tiba file Anda "tidak terkunci" saat Anda masih di tengah-tengah sesuatu, dan kemudian beberapa proses lain seperti cronjob mungkin menguncinya, dan kemudian Anda kehilangan mengunci dan permintaan Anda tidak dapat selesai ...
Darien
5

Ini mungkin bukan yang Anda cari, tetapi untuk mengetahui masalah dari sudut lain ....

Apakah kedua proses Java ini mungkin ingin mengakses file yang sama di aplikasi yang sama? Mungkin Anda bisa memfilter semua akses ke file melalui satu metode tersinkronisasi (atau, bahkan lebih baik, menggunakan JSR-166 )? Dengan begitu, Anda dapat mengontrol akses ke file, dan bahkan mungkin mengantrekan permintaan akses.

pkaeding
sumber
3
Dua proses tidak dapat menggunakan sinkronisasi, hanya dua utas dalam proses yang sama.
Marquis dari Lorne
3

Gunakan RandomAccessFile, dapatkan salurannya, lalu panggil lock (). Saluran yang disediakan oleh aliran masukan atau keluaran tidak memiliki hak yang cukup untuk mengunci dengan benar. Pastikan untuk memanggil unlock () di blok terakhir (menutup file tidak selalu membuka kunci).

Kevin Day
sumber
bisakah kamu menjelaskan? Maksud saya, sejauh mana penguncian oleh RandomAccess File lebih baik atau lebih aman daripada streaming satu
Paralife
Tautan ke contoh sederhana yang diposting di bawah ini
Touko
Paralife - maaf atas keterlambatannya - baru saja memperhatikan pertanyaan Anda. Kunci dari aliran akan menjadi kunci baca (untuk aliran input) dan kunci tulis saluran penuh yang eksklusif (untuk aliran output). Pengalaman saya adalah bahwa kunci dari RAF memungkinkan kontrol yang lebih halus (yaitu Anda dapat mengunci bagian dari sebuah file).
Kevin Day
1

Di bawah ini adalah contoh kode potongan untuk mengunci file sampai prosesnya dilakukan oleh 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
sumber