AtomicInteger recordNumber = new AtomicInteger();
Files.lines(inputFile.toPath(), StandardCharsets.UTF_8)
.map(record -> new Record(recordNumber.incrementAndGet(), record))
.parallel()
.filter(record -> doSomeOperation())
.findFirst()
Когда я писал это, я предполагал, что потоки будут порождаться только вызовом карты, так как параллель помещается после карты. Но некоторые строки в файле получали разные номера записей для каждого выполнения.
Я прочитал официальную документацию по Java- потокам и несколько веб-сайтов, чтобы понять, как потоки работают под капотом.
Несколько вопросов:
Параллельный поток Java работает на основе SplitIterator , который реализуется каждой коллекцией, такой как ArrayList, LinkedList и т. Д. Когда мы создаем параллельный поток из этих коллекций, соответствующий итератор разделения будет использоваться для разделения и итерации коллекции. Это объясняет, почему параллелизм произошел на уровне исходного входного источника (строк файла), а не в результате карты (т. Е. Record pojo). Правильно ли мое понимание?
В моем случае вход является потоком ввода-вывода файла. Какой разделенный итератор будет использоваться?
Неважно, где мы находимся
parallel()
в трубопроводе. Исходный источник ввода всегда будет разделен, а остальные промежуточные операции будут применены.В этом случае Java не должна позволять пользователям размещать параллельные операции в любом месте конвейера, за исключением исходного источника. Потому что это дает неправильное понимание тем, кто не знает, как работает поток Java внутри. Я знаю, что
parallel()
операция была бы определена для типа объекта Stream, поэтому она работает таким образом. Но лучше предложить альтернативное решение.В приведенном выше фрагменте кода я пытаюсь добавить номер строки для каждой записи во входном файле, и поэтому он должен быть упорядочен. Тем не менее, я хочу применять
doSomeOperation()
параллельно, поскольку это тяжелый вес логики. Единственный способ добиться этого - написать собственный настраиваемый итератор разбиения. Есть ли другой путь?
Stream
непосредственно в интерфейсе, и из-за хорошего каскадирования каждая операция возвращается Stream
снова. Представьте, что кто-то хочет дать вам, Stream
но уже применил пару подобных операций map
. Вы, как пользователь, все еще хотите иметь возможность решить, будет ли он выполняться параллельно или нет. Таким образом, у вас должна быть возможность звонить parallel()
еще, хотя поток уже существует.
flatMap
или выполняете небезопасные методы или подобные.
Path
находится в локальной файловой системе и вы используете недавний JDK, сплитератор будет иметь лучшую возможность параллельной обработки, чем пакетное умножение на 1024. Но в некоторых findFirst
сценариях сбалансированное разбиение может быть даже контрпродуктивным …
parallel()
это не что иное, как общий запрос модификатора, который применяется к базовому объекту потока. Помните, что существует только один поток-источник, если вы не применяете конечные операции к каналу, т. Е. До тех пор, пока ничего не «выполнено». Сказав это, вы в основном просто ставите под сомнение выбор дизайна Java. Который основан на мнении, и мы не можем помочь с этим.