Як я можу заблокувати файл за допомогою Java (якщо можливо)


121

У мене є процес Java, який відкриває файл за допомогою FileReader. Як я можу запобігти іншому процесу (Java) відкрити цей файл або принаймні повідомити той другий процес, що файл уже відкритий? Чи автоматично це робить другий процес винятком, якщо файл відкритий (що вирішує мою проблему) або я повинен явно відкрити його в першому процесі за допомогою якогось прапора чи аргументу?

Для уточнення:

У мене є програма Java, яка містить список папок і відкриває кожен файл у списку для його обробки. Він обробляє кожен файл за іншим. Обробка кожного файлу полягає у його читанні та виконанні деяких обчислень на основі вмісту і займає близько 2 хвилин. У мене також є інша програма Java, яка робить те саме, але замість цього пише у файл. Те, що я хочу, - це можливість одночасно запускати ці програми, так що сценарій буде таким. ReadApp перераховує папку та знаходить файли A, B, C. Він відкриває файл A і починає читання. WriteApp перераховує папку та знаходить файли A, B, C. Він відкриває файл A, бачить, що він є відкритим (за винятком чи будь-яким іншим способом) і переходить у файл B. ReadApp закінчує файл A і продовжує B. Він бачить, що це відкрито і продовжує C. Важливо, що WriteApp не робить ' t пишіть, поки ReadApp читає той самий файл або навпаки. Вони різні процеси.


12
Ви маєте на увазі "процес", як у процесі (два JVM) або потік (той же JVM). Вплив на відповідь є першорядним.
Стю Томпсон

перевірити зразок коду, що показує рішення тут: stackoverflow.com/a/58871479/5154619
Davi Cavalcanti

Відповіді:


117

FileChannel.lock - це, мабуть, те, що ви хочете.

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

(Відмова: Код не скомпільовано і, звичайно, не перевірено.)

Зверніть увагу на розділ "Залежності від платформи" в документі API для FileLock .


22
Що ще важливіше, розумійте, що блокування для JVM, а не підходить для блокування файлу для доступу окремими потоками в межах одного JVM.
Стю Томпсон,

11
Вам потрібен потік для запису (тобто FileOutputStream).
Хав'єр

@Javier Ти? Я не пробував. Нічого не вискакує з документів API, кажучи, що це вимога. FileOutputStreamне буде багато користі для Reader.
Том Хотін - тайклін

18
Так, я спробував це, і це кидає NonWritableChannelException, тому що lock()намагається придбати ексклюзивний замок, але це вимагає доступу до запису. Якщо у вас є вхідний потік, ви можете використовувати lock(0L, Long.MAX_VALUE, false)який отримує загальний замок і вимагає лише доступу для читання. Ви також можете використовувати RandomAccessFileвідкритий у режимі читання-запису, якщо хочете ексклюзивного блокування під час читання ..., але це заборонятиме одночасним читачам.
Хав'єр

6
@Javier Я думаю, ви хочете сказати lock(0L, Long.MAX_VALUE, true), ні lock(0L, Long.MAX_VALUE, false). останній аргумент є boolean shared docs.oracle.com/javase/8/docs/api/java/nio/channels/…
Джон sullivan

60

Не використовуйте класи в java.ioпакеті, а використовуйте java.nioпакет. Останній має FileLockклас. Ви можете застосувати блокування до 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, я записую у файл блокування поточний під із цієї підказки stackoverflow.com/a/35885/1422630 , тож після того, як я зможу прочитати його у новому екземплярі!
Сила Водолія

1
цей виглядав добре, але це не працює. Я отримую OverlappingFileLockException кожного разу, навіть коли файл навіть не існував
Гавриель,

1
Проблема буде, якщо ви зателефонуєте на tryLock після блокування, як написано в прикладі
Ігор Вукович

17

Якщо ви можете використовувати Java NIO ( JDK 1.4 або новішої версії ), то, я думаю, ви шукаєтеjava.nio.channels.FileChannel.lock()

FileChannel.lock ()


5
Може бути. Залежить від того, що ОП означає «процес». "Блокування файлів проводиться від імені всієї віртуальної машини Java. Вони не підходять для контролю доступу до файлу декількома потоками в межах однієї віртуальної машини."
Стю Томпсон,

@Stu: Я знаю, що ти давно відповів на це питання, але сподіваюся, ти зможеш уточнити, що ти маєш на увазі, коли ти сказавFile 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 Він посилається на документи: download.oracle.com/javase/6/docs/api/java/nio/channels/… Це означає, що це не видно для потоків, але впливає на інші процеси.
Артур Чайка

@Harry: Щоб додати ще більше до цих некрологічних коментарів, уявіть, що ви використовуєте Java для розміщення веб-сайтів із Tomcat. У вас може бути багато потоків, кожна з яких подає один запит від веб-браузера. Однак вони контролюють один і той же механізм блокування файлів, як і занадто багато кухарів на кухні. Один запит може закінчитися в середині другого, і раптом ваш файл "розблокується", поки ви все ще знаходитесь в середині чогось, а потім якийсь інший процес, наприклад, cronjob, може заблокувати його, і тоді ви несподівано втратили свій замок, і ваш запит не може закінчитися ...
Дарієн


5

Це може бути не те, що ви шукаєте, але в інтересах прийти до проблеми під іншим кутом зору ....

Це два процеси Java, які, можливо, хочуть отримати доступ до одного файлу в одній програмі? Можливо, ви можете просто відфільтрувати весь доступ до файлу за допомогою одного, синхронізованого методу (або, ще краще, за допомогою JSR-166 )? Таким чином, ви можете контролювати доступ до файлу та, можливо, навіть запити доступу до черги.


3
Два процеси не можуть використовувати синхронізацію, лише два потоки в одному і тому ж процесі.
Маркіз Лорн

3

Використовуйте RandomAccessFile, отримайте його канал, а потім вимкніть блокування (). Канал, наданий потоками введення або виведення, не має достатніх привілеїв для належного блокування. Не забудьте зателефонувати unlock () в остаточному блоці (закриття файлу не обов'язково звільняє замок).


чи можете ви докладно? Я маю на увазі, наскільки розблокування файлу RandomAccess краще або безпечніше, ніж один потоки
Paralife

Посилання на простий приклад, розміщений нижче
Touko

Параліфе - вибачте за затримку - щойно помітив ваше запитання. Блоки з потоків збираються блоками зчитування (для вхідних потоків) та ексклюзивними, повноканальними блоками запису (для вихідних потоків). Мій досвід полягав у тому, що блокування від RAF забезпечує більш точний контроль зерна (тобто ви можете заблокувати частини файлу).
День Кевіна

1

Нижче наведено зразок коду фрагмента для блокування файлу, поки його процес не виконає 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();
        }
    }

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.