Как заблокировать файл с помощью java (если возможно)

У меня есть процесс Java, который открывает файл с помощью FileReader. Как я могу предотвратить открытие этого файла другим (Java) процессом или, по крайней мере, уведомить этот второй процесс о том, что файл уже открыт? Это автоматически приводит к тому, что второй процесс получает исключение, если файл открыт (что решает мою проблему), или мне нужно явно открывать его в первом процессе с каким-то флагом или аргументом?

Чтобы уточнить:

У меня есть приложение Java, которое перечисляет папку и открывает каждый файл в списке для его обработки. Он обрабатывает каждый файл за другим. Обработка каждого файла состоит из его чтения и выполнения некоторых вычислений на основе содержимого, и это занимает около 2 минут. У меня также есть другое приложение Java, которое делает то же самое, но вместо этого записывает в файл. Я хочу иметь возможность запускать эти приложения одновременно, чтобы сценарий выглядел следующим образом. ReadApp выводит список папок и находит файлы A, B, C. Он открывает файл A и начинает чтение. WriteApp выводит список папок и находит файлы A, B, C. Он открывает файл A, видит, что он открыт (по исключению или другим способом), и переходит к файлу B. ReadApp завершает файл A и переходит к B. Он видит, что он открыт и продолжает C. Очень важно, чтобы WriteApp не писал, пока ReadApp читает тот же файл, или наоборот. Это разные процессы.


person Paralife    schedule 24.09.2008    source источник
comment
Вы имеете в виду «процесс», как в процессе (две JVM) или потоке (та же JVM). Влияние на ответ имеет первостепенное значение.   -  person Stu Thompson    schedule 25.09.2008
comment
проверьте пример кода, показывающий решение здесь: stackoverflow.com/a/58871479/5154619   -  person Davi Cavalcanti    schedule 15.11.2019


Ответы (8)


FileChannel.lock - это, вероятно, то, что вам нужно.

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

(Отказ от ответственности: код не компилируется и, конечно же, не тестируется.)

Обратите внимание на раздел «зависимости платформы» в Документация по API для FileLock.

person Tom Hawtin - tackline    schedule 24.09.2008
comment
Что еще более важно, поймите, что блокировка для JVM не подходит для блокировки файла для доступа отдельных потоков в пределах одной JVM. - person Stu Thompson; 25.09.2008
comment
Вам нужен доступный для записи поток (например, FileOutputStream). - person Javier; 16.04.2013
comment
@ Хавьер А ты? Я не пробовал. Из документации API ничего не выходит, что это требование. FileOutputStream не принесет большого пользы для Reader. - person Tom Hawtin - tackline; 17.04.2013
comment
Да, я пробовал, и он выдает NonWritableChannelException, потому что lock() пытается получить эксклюзивную блокировку, но для этого требуется доступ на запись. Если у вас есть поток input, вы можете использовать lock(0L, Long.MAX_VALUE, false), который получает общую блокировку и требует только доступа для чтения. Вы также можете использовать RandomAccessFile, открытый в режиме чтения-записи, если вам нужна эксклюзивная блокировка при чтении ... но это запретит одновременное чтение. - person Javier; 17.04.2013
comment
@StuThompson, в Windows это фактически блокирует и другие потоки. В Linux это не блокируется, если другой процесс / поток также не использует явную блокировку. - person Piotr Findeisen; 29.04.2015
comment
@ Хавьер Я думаю, ты хочешь сказать lock(0L, Long.MAX_VALUE, true), а не lock(0L, Long.MAX_VALUE, false). последний там аргумент boolean shared docs.oracle.com/javase/8/docs/api/java/nio/channels/ - person john sullivan; 23.01.2017
comment
@PiotrFindeisen Что здесь подразумевается под явной блокировкой? - person Dilip Raj Baral; 05.06.2019

Не используйте классы в пакете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) {
    }
person ayengin    schedule 26.01.2012
comment
кстати, я пишу в файле блокировки текущий pid из этого совета stackoverflow.com/a/35885/1422630, поэтому после прочту на новом экземпляре! - person Aquarius Power; 02.02.2015
comment
этот выглядел хорошо, но не работает. Я получаю исключение OverlappingFileLockException каждый раз, даже если файл даже не существует - person Gavriel; 05.02.2015
comment
Проблема произойдет, если вы вызовете tryLock после блокировки, как написано в примере - person Igor Vuković; 22.03.2016
comment
Для блокировки, как правило, важно выполнить снятие блокировки в конце, чтобы убедиться, что она разблокирована, даже если есть исключение. Было бы неплохо, если бы вы могли это исправить, чтобы люди не создавали ошибок. Хорошо, выражение try with resources в принятом ответе, вероятно, обычно даже лучше. - person Hans-Peter Störr; 30.04.2021

Если вы можете использовать Java NIO (JDK 1.4 или выше), то я думаю, вы ищете java.nio.channels.FileChannel.lock()

FileChannel.lock ()

person KC Baltz    schedule 24.09.2008
comment
Может быть. Зависит от того, что OP имел в виду под словом «процесс». Блокировки файлов выполняются от имени всей виртуальной машины Java. Они не подходят для управления доступом к файлу несколькими потоками в одной виртуальной машине. - person Stu Thompson; 25.09.2008
comment
@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 - person Thang Pham; 13.06.2011
comment
@ Гарри. Он цитирует документы: скачать. oracle.com/javase/6/docs/api/java/nio/channels/ Это означает, что он невидим для потоков, но влияет на другие процессы. - person Artur Czajka; 04.07.2011
comment
@Harry: Чтобы добавить еще больше к этим некро-комментариям, представьте, что вы используете Java для обслуживания веб-сайтов с помощью Tomcat. У вас может быть много потоков, каждый из которых обслуживает один запрос из веб-браузера. Однако все они управляют одним и тем же механизмом блокировки файлов, как и многие повара на кухне. Один запрос мог завершиться в середине второго, и внезапно ваш файл был разблокирован, пока вы все еще находились в середине чего-то, а затем какой-то другой процесс, такой как cronjob, мог заблокировать его, и тогда вы неожиданно потеряли свою блокировку и ваш запрос не может быть выполнен ... - person Darien; 09.07.2011

используйте java.nio.channels.FileLock в сочетании с java.nio.channels.FileChannel

person Matt    schedule 24.09.2008

Возможно, это не то, что вы ищете, но чтобы взглянуть на проблему под другим углом ...

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

person pkaeding    schedule 25.09.2008
comment
Два процесса не могут использовать синхронизацию, только два потока в одном процессе. - person user207421; 17.11.2015

Используйте RandomAccessFile, получите его канал, затем вызовите lock (). Канал, предоставляемый потоками ввода или вывода, не имеет достаточных прав для правильной блокировки. Обязательно вызовите unlock () в блоке finally (закрытие файла не обязательно снимает блокировку).

person Kevin Day    schedule 25.09.2008
comment
можешь уточнить? Я имею в виду, насколько блокировка RandomAccess File лучше или безопаснее, чем потоковая - person Paralife; 25.09.2008
comment
Ссылка на простой пример, размещенный ниже - person Touko; 27.08.2009
comment
Paralife - извините за задержку - только что заметил ваш вопрос. Блокировки потоков будут блокировками чтения (для входных потоков) и эксклюзивными блокировками записи всего канала (для выходных потоков). По моему опыту, блокировки из RAF позволяют более детально контролировать (то есть вы можете блокировать части файла). - person Kevin Day; 28.08.2009

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

}
person Ajay Kumar    schedule 24.05.2017
comment
Java 11 была выпущена в 2011 г. - person Arneball; 30.01.2021

Используйте это для unix, если вы переносите с помощью winscp или ftp:

public static void isFileReady(File entry) throws Exception {
        long realFileSize = entry.length();
        long currentFileSize = 0;
        do {
            try (FileInputStream fis = new FileInputStream(entry);) {
                currentFileSize = 0;
                while (fis.available() > 0) {
                    byte[] b = new byte[1024];
                    int nResult = fis.read(b);
                    currentFileSize += nResult;
                    if (nResult == -1)
                        break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("currentFileSize=" + currentFileSize + ", realFileSize=" + realFileSize);
        } while (currentFileSize != realFileSize);
    }
person Vamsi    schedule 12.08.2020