У меня есть пакетный процесс, работающий под java JDK 1.7. Он работает в системе с RHEL, 2.6.18-308.el5 #1 SMP.
Этот процесс получает список объектов метаданных из базы данных. Из этих метаданных он извлекает путь к файлу. Этот файл может существовать, а может и не существовать.
Процесс использует ExecutorService (Executors.newFixedThreadPool()
) для запуска нескольких потоков. Каждый поток запускает Callable, который запускает процесс, который читает этот файл и записывает другой файл, если этот входной файл существует (и регистрирует результат), и ничего не делает, если файл не существует (за исключением регистрации этого результата).
Я нахожу поведение неопределенным. Хотя фактическое существование каждого из файлов постоянно, выполнение этого процесса не дает последовательных результатов. Обычно он дает правильные результаты, но иногда обнаруживает, что некоторые файлы, которые действительно существуют, отсутствуют. Если я снова запущу тот же процесс, он обнаружит файлы, которые ранее не существовали.
Почему это может происходить, и есть ли альтернативный способ сделать это более надежным? Является ли ошибкой запись файлов в многопоточном процессе, в то время как другие потоки пытаются прочитать каталог? Поможет ли меньший пул потоков (в настоящее время 30)?
ОБНОВЛЕНИЕ: вот фактический код процесса unix, вызываемого рабочими потоками в этом сценарии:
public int convertOutputFile(String inputFile, String outputFile)
throws IOException
{
List<String> args = new LinkedList<String>();
args.add("sox");
args.add(inputFile);
args.add(outputFile);
args.addAll(2, this.outputArguments);
args.addAll(1, this.inputArguments);
long pStart = System.currentTimeMillis();
int status = -1;
Process soxProcess = new ProcessBuilder(args).start();
try {
// if we don't wait for the process to complete, player won't
// find the converted file.
status = soxProcess.waitFor();
if (status == 0) {
logger.debug(String.format("SoX conversion process took %d ms.",
System.currentTimeMillis() - pStart));
} else {
logger.error("SoX conversion process returned an error status of " + status);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return status;
}
ОБНОВЛЕНИЕ №2:
Я попробовал эксперимент по переключению с java.io.File.exists() на java.nio.Files.exists(), и это, кажется, обеспечивает большую надежность. Мне еще предстоит увидеть состояние отказа при нескольких попытках, тогда как раньше это происходило примерно в 10% случаев. Итак, я думаю, я хочу узнать, является ли версия nio более надежной в том, как она обрабатывает базовую файловую систему. Этот вывод позже оказался ложным. nio здесь не поможет.
ОБНОВЛЕНИЕ №3: при дальнейшем рассмотрении я все еще обнаруживаю, что происходит то же самое сбойное состояние. Так что переход на nio не панацея. Я получил лучшие результаты, уменьшив размер пула потоков службы-исполнителя до 1. Это кажется более надежным, и таким образом нет шансов, что один поток будет читать каталог, в то время как другой поток запускает процесс, который записывает в тот же каталог.
Еще одна возможность, которую я еще не исследовал, заключается в том, будет ли лучше, если я поместю выходные файлы в другой каталог, чем входные файлы. Я поместил их в один и тот же каталог, потому что его было проще кодировать, но это может сбивать с толку, поскольку создание выходного файла влияет на тот же каталог, что и сканирование входного каталога.
ОБНОВЛЕНИЕ №4: перекодирование таким образом, чтобы выходные файлы записывались в другой каталог, чем входные файлы (существование которых проверяется), особо не помогает. Единственное изменение, которое помогает, — размер пула потоков ExecutorService, равный 1, другими словами, отсутствие многопоточности в этой операции.
ps --forest aux | grep java
, не было бы 30 процессов jvm, только один. - person MeetTitan   schedule 07.01.2016Runtime.exec
, вы определенно не создаете процессы, а вместо этого создаете потоки. Многопроцессорность != многопоточность. - person MeetTitan   schedule 07.01.2016