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




Если вы можете использовать Java NIO (JDK 1.4 или выше), то я думаю, вы ищете java.nio.channels.FileChannel.lock()
Может быть. Зависит от того, что OP имел в виду под словом «процесс». «Блокировки файлов устанавливаются от имени всей виртуальной машины 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.
@Harry Он цитирует документы: download.oracle.com/javase/6/docs/api/java/nio/channels/… Это означает, что он невидим для потоков, но влияет на другие процессы.
@Harry: Чтобы добавить еще больше к этим некро-комментариям, представьте, что вы используете Java для обслуживания веб-сайтов с помощью Tomcat. У вас может быть много потоков, каждый из которых обслуживает один запрос из веб-браузера. Однако все они управляют одним и тем же механизмом блокировки файлов, как и многие повара на кухне. Один запрос может завершиться в середине второго, и внезапно ваш файл «разблокируется», пока вы все еще заняты чем-то, а затем какой-то другой процесс, такой как cronjob, может заблокировать его, и тогда вы неожиданно потеряли свой блокировка и ваш запрос не может быть завершен ...
используйте java.nio.channels.FileLock вместе с java.nio.channels.FileChannel
FileChannel.lock - это, вероятно, то, что вам нужно.
try (
FileInputStream in = new FileInputStream(file);
java.nio.channels.FileLock lock = in.getChannel().lock();
Reader reader = new InputStreamReader(in, charset)
) {
...
}
(Отказ от ответственности: код не компилируется и, конечно же, не тестируется.)
Обратите внимание на раздел «зависимости платформы» в Документация по API для FileLock.
Что еще более важно, поймите, что блокировка для JVM не подходит для блокировки файла для доступа отдельных потоков в пределах одной JVM.
Вам нужен записываемый поток (например, FileOutputStream).
@ Хавьер А ты? Я не пробовал. Из документации API ничего не выходит, что это требование. FileOutputStream вряд ли пригодится Reader.
Да, я пробовал, и он выдает NonWritableChannelException, потому что lock() пытается получить монопольную блокировку, но для этого требуется доступ на запись. Если у вас есть поток Вход, вы можете использовать lock(0L, Long.MAX_VALUE, false), который получает разделяемую блокировку и требует только доступа для чтения. Вы также можете использовать RandomAccessFile, открытый в режиме чтения-записи, если вам нужна эксклюзивная блокировка при чтении ... но это запретит одновременное чтение.
@StuThompson, в Windows это фактически блокирует и другие потоки. В Linux это не блокируется, если другой процесс / поток также не использует явную блокировку.
@ Хавьер Я думаю, вы хотите сказать lock(0L, Long.MAX_VALUE, true), а не lock(0L, Long.MAX_VALUE, false). последний аргумент там boolean shareddocs.oracle.com/javase/8/docs/api/java/nio/channels/…
@PiotrFindeisen Что здесь подразумевается под явной блокировкой?
Используйте RandomAccessFile, получите его канал, затем вызовите lock (). Канал, предоставляемый потоками ввода или вывода, не имеет достаточных привилегий для правильной блокировки. Обязательно вызовите unlock () в блоке finally (закрытие файла не обязательно снимает блокировку).
можешь уточнить? Я имею в виду, насколько блокировка RandomAccess File лучше или безопаснее, чем потоковая
Ссылка на простой пример, размещенный ниже
Paralife - извините за задержку - только что заметил ваш вопрос. Блокировки потоков будут блокировками чтения (для входных потоков) и эксклюзивными блокировками записи всего канала (для выходных потоков). По моему опыту, блокировки из RAF позволяют более детально контролировать (то есть вы можете блокировать части файла).
Возможно, это не то, что вы ищете, но чтобы взглянуть на проблему под другим углом ...
Могут ли эти два процесса Java получить доступ к одному и тому же файлу в одном приложении? Возможно, вы можете просто отфильтровать весь доступ к файлу с помощью одного синхронизированного метода (или, что еще лучше, с помощью JSR-166)? Таким образом, вы можете контролировать доступ к файлу и, возможно, даже ставить запросы доступа в очередь.
Два процесса не могут использовать синхронизацию, только два потока в одном процессе.
Не используйте классы в пакете 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) {
}
кстати, я записываю в файл блокировки текущий pid из этой подсказки stackoverflow.com/a/35885/1422630, поэтому после того, как я смогу прочитать его в новом экземпляре!
этот выглядел хорошо, но не работает. Я получаю исключение OverlappingFileLockException каждый раз, даже если файл даже не существует
Проблема произойдет, если вы вызовете tryLock после блокировки, как написано в примере
Ниже приведен пример кода фрагмента для блокировки файла до тех пор, пока он не будет обработан 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();
}
}
}
Java 11 была выпущена в 2011 г.
Используйте это для 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);
}
Вы имеете в виду «процесс», как в процесс (две JVM) или поток (та же JVM). Влияние на ответ имеет первостепенное значение.