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 тощо. Коли ми будуємо паралельний потік із цих колекцій, відповідний ітератор розділення буде використаний для розділення та ітерації колекції. Це пояснює, чому паралелізм траплявся на рівні вихідного джерела входу (рядки файлів), а не в результаті відображення на карті (тобто записувати pojo). Чи правильно моє розуміння?
У моєму випадку вхід - це потік IO файлів. Який ітератор розділення буде використовуватися?
Не має значення, де ми розміщуємось
parallel()у трубопроводі. Початкове вхідне джерело завжди буде розділене, а інші проміжні операції будуть застосовані.У цьому випадку Java не повинна дозволяти користувачам проводити паралельні операції в будь-якому місці трубопроводу, за винятком початкового джерела. Тому що це дає неправильне розуміння тим, хто не знає, як java stream працює всередині. Я знаю, що
parallel()операція була б визначена для типу об'єкта Stream, і так, вона працює таким чином. Але, краще надати якесь альтернативне рішення.У наведеному вище фрагменті коду я намагаюся додати номер рядка до кожної записи у вхідному файлі, і тому його слід замовити. Однак я хочу застосувати
doSomeOperation()паралельно, оскільки це логіка великої ваги. Один із способів досягти - написати власний індивідуальний ітератор розділення. Чи є інший спосіб?
Streamінтерфейсі безпосередньо, і через приємне каскадування кожна операція повертається Streamзнову. Уявіть, що хтось хоче вам надати, Streamале вже застосував пару операцій на кшталт mapцього. Ви, як користувач, все ще хочете мати можливість вирішити, паралельно це виконувати чи ні. Отже, ви повинні мати можливість parallel()ще дзвонити , хоча потік уже існує.
flatMapабо виконайте небезпечні методи для потоків чи подібні.
Pathлокальній файловій системі ви використовуєте недавній JDK, сплітератор матиме кращу можливість паралельної обробки, ніж пакетне множення 1024. Але збалансоване розщеплення може бути навіть контрпродуктивним у деяких findFirstсценаріях…
parallel()є не що інше, як загальний запит на модифікатор, який застосовується до базового об'єкта потоку. Пам'ятайте, що є лише один джерело-потік, якщо ви не застосовуєте остаточні операції до труби, тобто до тих пір, поки нічого не буде "виконано". Сказавши це, ви в основному ставите під сумнів вибір дизайну Java. Що ґрунтується на думці, і ми не можемо в цьому допомогти.