Отсутствие shared mutable state
Параллелизм превращает скрытые баги в явные катастрофы. Код, работающий корректно в последовательном потоке, может давать неверные результаты или зависать при parallel().
Race condition в accumulator:
AtomicInteger обеспечивает корректность, но каждый incrementAndGet() требует атомарной операции и кэш-коэрентности между ядрами. При высоком contention производительность падает ниже последовательной версии.
Непотокобезопасная коллекция в collect:
Стандартный toList() использует Collector с правильным combiner, создающим локальные ArrayList для каждого сегмента и сливающим их в конце. Прямое использование ArrayList::new в collect нарушает этот протокол.
Изменяемые ключи в groupingBy:
Параллелизм увеличивает вероятность одновременного доступа к изменяемым структурам. Иммутабельность ключей и элементов становится не рекомендацией, а требованием.
#Java #для_новичков #beginner #stream_api #ForkJoinPool #parallelStream
Параллелизм превращает скрытые баги в явные катастрофы. Код, работающий корректно в последовательном потоке, может давать неверные результаты или зависать при parallel().
Race condition в accumulator:
// Антипаттерн: общий счётчик
AtomicInteger counter = new AtomicInteger(0);
List<Result> results = data.parallelStream()
.map(d -> {
counter.incrementAndGet(); // Потокобезопасен, но не бесплатен
return process(d);
})
.collect(toList());
AtomicInteger обеспечивает корректность, но каждый incrementAndGet() требует атомарной операции и кэш-коэрентности между ядрами. При высоком contention производительность падает ниже последовательной версии.
Непотокобезопасная коллекция в collect:
// Катастрофа: ArrayList не потокобезопасен
List<Result> results = data.parallelStream()
.map(this::process)
.collect(ArrayList::new, List::add, List::addAll); // Race condition!
Стандартный toList() использует Collector с правильным combiner, создающим локальные ArrayList для каждого сегмента и сливающим их в конце. Прямое использование ArrayList::new в collect нарушает этот протокол.
Изменяемые ключи в groupingBy:
// Опасность: изменяемый ключ после группировки
Map<MutableAuthor, List<Book>> byAuthor = books.parallelStream()
.collect(groupingBy(MutableAuthor::new));
// Позже в другом потоке...
byAuthor.keySet().iterator().next().setName("New"); // Непредсказуемое поведение
Параллелизм увеличивает вероятность одновременного доступа к изменяемым структурам. Иммутабельность ключей и элементов становится не рекомендацией, а требованием.
#Java #для_новичков #beginner #stream_api #ForkJoinPool #parallelStream
👍4
Отсутствие барьеров в конвейере
Барьер (synchronization point) — операция, требующая видимости всех элементов или глобальной координации. Барьеры разрушают параллелизм, заставляя воркеров ждать друг друга.
sorted: полный барьер
Операция sorted() требует всех элементов для сортировки. В параллельном потоке каждый сегмент сортируется локально, затем результаты сливаются (merge) в глобально отсортированную последовательность. Слияние требует координации и дополнительной памяти.
Если конвейер содержит sorted без последующих операций, параллелизм может быть оправдан для дорогих предшествующих операций. Но sorted + limit — особенно неэффективная комбинация: все элементы сортируются, хотя нужны только первые N.
limit и findFirst: частичные барьеры
limit(n) в параллельном потоке создаёт глобальный счётчик оставшихся элементов. Когда один воркер достигает лимита, другие должны быть уведомлены для остановки. Это требует синхронизации и снижает эффективность.
findFirst() требует упорядоченности. Если источник ORDERED, воркеры должны координироваться для определения, кто нашёл "первый" элемент.
Если источник неупорядочен или порядок не важен, findAny() предпочтительнее — он возвращает любой найденный элемент без координации.
distinct: барьер с состоянием
distinct() в параллельном потоке требует глобального множества уникальных элементов, доступного всем воркерам. Реализация использует ConcurrentHashMap, что добавляет накладные расходы на синхронизацию. Для больших потоков с высокой кардинальностью это может быть медленнее последовательной версии с HashSet.
Когда parallelStream применим
Суммируя критерии, эффективный сценарий для parallelStream:
Источник: ArrayList, массив, IntStream.range с большим размером (10 000+ элементов)
Операция: CPU-bound, дорогая (> 10 мкс), без блокировок
Конвейер: stateless операции, без sorted, limit, distinct или с ними в конце
Данные: иммутабельные, без shared mutable state
Цель: агрегация в коллектор с эффективным combiner
Пример подходящей задачи: анализ миллиона документов, извлечение признаков, подсчёт статистики.
Пример неподходящей задачи: фильтрация списка идентификаторов с простым предикатом, преобразование в строки, лимит первых десяти.
Измерение прежде оптимизации
Предположения о производительности часто ошибочны. Единственный надёжный метод — измерение с репрезентативными данными:
Микробенчмарки без JMH ненадёжны из-за JIT-оптимизаций, GC-пauses и прогрева кэша. Реальное приложение требует мониторинга в production: метрики latency, throughput, использование CPU, профилирование hot paths.
#Java #для_новичков #beginner #stream_api #ForkJoinPool #parallelStream
Барьер (synchronization point) — операция, требующая видимости всех элементов или глобальной координации. Барьеры разрушают параллелизм, заставляя воркеров ждать друг друга.
sorted: полный барьер
Операция sorted() требует всех элементов для сортировки. В параллельном потоке каждый сегмент сортируется локально, затем результаты сливаются (merge) в глобально отсортированную последовательность. Слияние требует координации и дополнительной памяти.
// Барьер: локальная сортировка + глобальное слияние
List<Book> sorted = books.parallelStream()
.sorted(comparing(Book::year)) // Накладные расходы на merge
.collect(toList());
Если конвейер содержит sorted без последующих операций, параллелизм может быть оправдан для дорогих предшествующих операций. Но sorted + limit — особенно неэффективная комбинация: все элементы сортируются, хотя нужны только первые N.
limit и findFirst: частичные барьеры
limit(n) в параллельном потоке создаёт глобальный счётчик оставшихся элементов. Когда один воркер достигает лимита, другие должны быть уведомлены для остановки. Это требует синхронизации и снижает эффективность.
findFirst() требует упорядоченности. Если источник ORDERED, воркеры должны координироваться для определения, кто нашёл "первый" элемент.
Если источник неупорядочен или порядок не важен, findAny() предпочтительнее — он возвращает любой найденный элемент без координации.
// Плохо: упорядоченный источник + findFirst в parallel
Optional<Book> first = books.parallelStream()
.filter(b -> b.year() > 2000)
.findFirst(); // Требует проверки всех предшествующих сегментов
// Лучше: findAny для неупорядоченных задач
Optional<Book> any = books.parallelStream()
.filter(b -> b.year() > 2000)
.findAny(); // Первый найденный в любом сегменте
distinct: барьер с состоянием
distinct() в параллельном потоке требует глобального множества уникальных элементов, доступного всем воркерам. Реализация использует ConcurrentHashMap, что добавляет накладные расходы на синхронизацию. Для больших потоков с высокой кардинальностью это может быть медленнее последовательной версии с HashSet.
Когда parallelStream применим
Суммируя критерии, эффективный сценарий для parallelStream:
Источник: ArrayList, массив, IntStream.range с большим размером (10 000+ элементов)
Операция: CPU-bound, дорогая (> 10 мкс), без блокировок
Конвейер: stateless операции, без sorted, limit, distinct или с ними в конце
Данные: иммутабельные, без shared mutable state
Цель: агрегация в коллектор с эффективным combiner
Пример подходящей задачи: анализ миллиона документов, извлечение признаков, подсчёт статистики.
Пример неподходящей задачи: фильтрация списка идентификаторов с простым предикатом, преобразование в строки, лимит первых десяти.
Измерение прежде оптимизации
Предположения о производительности часто ошибочны. Единственный надёжный метод — измерение с репрезентативными данными:
// JMH-бенчмарк для сравнения
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class StreamBenchmark {
@State(Scope.Thread)
public static class Data {
List<Book> books = generateBooks(100000);
}
@Benchmark
public List<Result> sequential(Data d) {
return d.books.stream()
.map(this::expensiveTransform)
.collect(toList());
}
@Benchmark
public List<Result> parallel(Data d) {
return d.books.parallelStream()
.map(this::expensiveTransform)
.collect(toList());
}
}
Микробенчмарки без JMH ненадёжны из-за JIT-оптимизаций, GC-пauses и прогрева кэша. Реальное приложение требует мониторинга в production: метрики latency, throughput, использование CPU, профилирование hot paths.
#Java #для_новичков #beginner #stream_api #ForkJoinPool #parallelStream
👍5
Что выведет код?
#Tasks
import java.util.*;
public class Task200326 {
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8));
List<Integer> list2 = new LinkedList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8));
long count1 = list1.parallelStream()
.map(x -> {
try { Thread.sleep(10); } catch (InterruptedException e) {}
return x * 2;
})
.count();
long count2 = list2.parallelStream()
.map(x -> {
try { Thread.sleep(10); } catch (InterruptedException e) {}
return x * 2;
})
.count();
System.out.println(count1 == count2);
System.out.println(list1.parallelStream().isParallel());
System.out.println(list2.parallelStream().isParallel());
}
}
#Tasks
👍3
Варианты ответа:
Anonymous Quiz
43%
true true true
0%
true false true
14%
false true true
43%
true true false
👍2
Что такое инициализационные блоки (initializer blocks)? 🤓
Ответ:
В Java есть два типа блоков инициализации.
Статические блоки (static { ... }) выполняются один раз при загрузке класса, до создания любых объектов, и используются для инициализации статических переменных.
Нестатические (instance) блоки { ... } выполняются при создании каждого нового объекта, перед вызовом конструктора. Они выполняются в порядке объявления. Instance-блоки удобны, когда у класса несколько конструкторов и нужно выполнить общий для всех код.
#собеседование
Ответ:
Статические блоки (static { ... }) выполняются один раз при загрузке класса, до создания любых объектов, и используются для инициализации статических переменных.
Нестатические (instance) блоки { ... } выполняются при создании каждого нового объекта, перед вызовом конструктора. Они выполняются в порядке объявления. Instance-блоки удобны, когда у класса несколько конструкторов и нужно выполнить общий для всех код.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
История технологий сегодня — 21 марта
ℹ️ Кто родился в этот день
Барон (1809) Жан-Бати́ст Жозе́ф Фурье́ (фр. Jean-Baptiste Joseph Fourier; 21 марта 1768, Осер, Франция — 16 мая 1830, Париж) — французский математик и физик. Нашёл формулу представления функции с помощью интеграла, играющую важную роль в современной математике. Доказал, что всякую произвольно начерченную линию, составленную из отрезков дуг разных кривых, можно представить единым аналитическим выражением. В 1823 году независимо от Эрстеда открыл термоэлектрический эффект, показал, что он обладает свойством суперпозиции, создал термоэлектрический элемент.
🌐 Знаковые события
1924 — 24-летний физик Вольфганг Паули в опубликованной статье сформулировал один из важнейших принципов современной теоретической физики, за который в 1945 году получил Нобелевскую премию.
1940 — Учёные Отто Фриш и Рудольф Пайерлс в Бирмингемском университете (Великобритания) сделали оценку критической массы урана-235.
#Biography #Birth_Date #Events #21марта
Барон (1809) Жан-Бати́ст Жозе́ф Фурье́ (фр. Jean-Baptiste Joseph Fourier; 21 марта 1768, Осер, Франция — 16 мая 1830, Париж) — французский математик и физик. Нашёл формулу представления функции с помощью интеграла, играющую важную роль в современной математике. Доказал, что всякую произвольно начерченную линию, составленную из отрезков дуг разных кривых, можно представить единым аналитическим выражением. В 1823 году независимо от Эрстеда открыл термоэлектрический эффект, показал, что он обладает свойством суперпозиции, создал термоэлектрический элемент.
1924 — 24-летний физик Вольфганг Паули в опубликованной статье сформулировал один из важнейших принципов современной теоретической физики, за который в 1945 году получил Нобелевскую премию.
1940 — Учёные Отто Фриш и Рудольф Пайерлс в Бирмингемском университете (Великобритания) сделали оценку критической массы урана-235.
#Biography #Birth_Date #Events #21марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
С 14.03 по 20.03
Предыдущий пост(с 07.03 по 13.03)
Воскресный мотивационный пост:
Не было мотивации, а вообще нужна?) (все равно это никто не читает)
Запись встреч/видео:
Видео в процессе подготовки... lvl2
Обучающие статьи:
Раздел 8. Stream API и функциональный стиль
Глава 5: Контракты. Почему equals, hashCode и иммутабельность — закон
(Практика): Саботаж и починка в «Библиотеке»
Глава 6: Parallel Stream
Механика ForkJoinPool.commonPool()
Условия эффективности параллельного stream
[Совет по Java #016]
Тема: Ленивая загрузка в Hibernate (FetchType.LAZY) не работает после закрытия сессии.
[Совет по Java #017]
Тема: ConcurrentModificationException при итерации и модификации коллекции
Полезные статьи и видео:
WebFlux, Virtual Threads и все-все-все
Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование
Предыдущий пост(с 07.03 по 13.03)
Воскресный мотивационный пост:
Не было мотивации, а вообще нужна?) (все равно это никто не читает)
Запись встреч/видео:
Видео в процессе подготовки... lvl2
Обучающие статьи:
Раздел 8. Stream API и функциональный стиль
Глава 5: Контракты. Почему equals, hashCode и иммутабельность — закон
(Практика): Саботаж и починка в «Библиотеке»
Глава 6: Parallel Stream
Механика ForkJoinPool.commonPool()
Условия эффективности параллельного stream
[Совет по Java #016]
Тема: Ленивая загрузка в Hibernate (FetchType.LAZY) не работает после закрытия сессии.
[Совет по Java #017]
Тема: ConcurrentModificationException при итерации и модификации коллекции
Полезные статьи и видео:
WebFlux, Virtual Threads и все-все-все
Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование
👍4🔥1
Потратил 6! часов, чтобы собрать настройки ретраев и CircuitBreaker в resilience4j в полноценно работающую демонстрацию...
И все для вас)))🧑💻
Скоро будет видео💃
И все для вас)))
Скоро будет видео
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7👍3🍾1 1
История технологий сегодня — 22 марта
ℹ️ Кто родился в этот день
Ро́берт Э́ндрюс Ми́лликен (англ. Robert Andrews Millikan; 22 марта 1868, Моррисон, Иллинойс, США — 19 декабря 1953, Сан-Марино, Калифорния, США) — американский физик. В 1923 году получил Нобелевскую премию по физике за работы в области фотоэлектрического эффекта и за измерения заряда электрона[4]. Впоследствии он занимался изучением космических лучей.
Бертон Ри́хтер (англ. Burton Richter; 22 марта 1931, Нью-Йорк, США — 18 июля 2018, Станфорд, штат Калифорния, США) — американский физик, лауреат Нобелевской премии по физике в 1976 году, совместно с Самюэлем Тингом «за основополагающий вклад в работу по открытию тяжёлой элементарной частицы нового типа».
🌐 Знаковые события
1895 — в Париже состоялась первая в истории публичная демонстрация кинофильма. Это был фильм братьев Люмьер «Выход рабочих с фабрики».
1902 — Британия и Персия заключили договор о соединении телеграфной линией Европы и Индии.
1904 — в лондонской газете «Иллюстрейтед Миррор» впервые в мире опубликована цветная фотография.
#Biography #Birth_Date #Events #22марта
Ро́берт Э́ндрюс Ми́лликен (англ. Robert Andrews Millikan; 22 марта 1868, Моррисон, Иллинойс, США — 19 декабря 1953, Сан-Марино, Калифорния, США) — американский физик. В 1923 году получил Нобелевскую премию по физике за работы в области фотоэлектрического эффекта и за измерения заряда электрона[4]. Впоследствии он занимался изучением космических лучей.
Бертон Ри́хтер (англ. Burton Richter; 22 марта 1931, Нью-Йорк, США — 18 июля 2018, Станфорд, штат Калифорния, США) — американский физик, лауреат Нобелевской премии по физике в 1976 году, совместно с Самюэлем Тингом «за основополагающий вклад в работу по открытию тяжёлой элементарной частицы нового типа».
1895 — в Париже состоялась первая в истории публичная демонстрация кинофильма. Это был фильм братьев Люмьер «Выход рабочих с фабрики».
1902 — Британия и Персия заключили договор о соединении телеграфной линией Европы и Индии.
1904 — в лондонской газете «Иллюстрейтед Миррор» впервые в мире опубликована цветная фотография.
#Biography #Birth_Date #Events #22марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
9. Retry vs Circuit Breaker: кто спасёт ваши сервисы от каскадных падений?
В этом видео мы на реальном примере разберём, как правильно комбинировать Retry и Circuit Breaker с помощью Resilience4j, чтобы сделать систему устойчивой к сбоям внешних зависимостей.
Что вас ждёт:
🔵 Retry — когда он полезен, а когда опасен.
🔵 Circuit Breaker — как он работает, зачем нужны состояния CLOSED, OPEN, HALF-OPEN и как его настраивать.
🔵 Разбор настройки через application.yml: скользящие окна, пороги ошибок, медленные вызовы, экспоненциальный backoff.
🔵 Сравнение Spring Retry и Resilience4j: что выбрать для production?
🔵 Готовые фрагменты кода и конфигурации для внедрения в ваш проект.
Исходный код проекта на GitHub очень ждет Ваших звезд☺️
Ссылка на Youtube
Ссылка на Рутьюб
Смотрите, ставьте лайки, подписывайтесь на каналы!✌️
❗️ ❗️ ❗️ Огромная просьба - если Вам понравилась моя работа, распространите эту серию по всем доступным вам местам: телеграм, discord и прочим каналам. ❗️ ❗️ ❗️
Буду крайне благодарен🙂
В этом видео мы на реальном примере разберём, как правильно комбинировать Retry и Circuit Breaker с помощью Resilience4j, чтобы сделать систему устойчивой к сбоям внешних зависимостей.
Что вас ждёт:
Исходный код проекта на GitHub очень ждет Ваших звезд
Ссылка на Youtube
Ссылка на Рутьюб
Смотрите, ставьте лайки, подписывайтесь на каналы!
Буду крайне благодарен
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
История технологий сегодня — 23 марта
ℹ️ Кто родился в этот день
Джин Э. Саммет (23 марта 1928 — 20 мая 2017) — американская учёная в области информатики, разработавшая язык программирования FORMAC в 1962 году. Она также была одним из разработчиков влиятельного языка программирования COBOL.
Вернер Магнус Максимилиан фрайхерр фон Браун (нем. Wernher Magnus Maximilian Freiherr von Braun; 23 марта 1912, Вирзиц, провинция Позен, Пруссия, Германская империя — 16 июня 1977, Александрия, Виргиния, США) — германский, а с 1955 года — американский конструктор ракетно-космической техники, один из основоположников современного ракетостроения, создатель первых баллистических ракет. Член НСДАП с 1937 года, штурмбаннфюрер СС (1943—1945). В США считается отцом американской космической программы.
🌐 Знаковые события
1876 — русский инженер Павел Яблочков запатентовал электрическую лампочку.
1983 — запущен «Астрон», советский космический ультрафиолетовый телескоп.
2020 — компания Valve выпустила компьютерную игру для устройств виртуальной реальности «Half-Life: Alyx» (на Windows) *Кто не играл - рекомендую
#Biography #Birth_Date #Events #23марта
Джин Э. Саммет (23 марта 1928 — 20 мая 2017) — американская учёная в области информатики, разработавшая язык программирования FORMAC в 1962 году. Она также была одним из разработчиков влиятельного языка программирования COBOL.
Вернер Магнус Максимилиан фрайхерр фон Браун (нем. Wernher Magnus Maximilian Freiherr von Braun; 23 марта 1912, Вирзиц, провинция Позен, Пруссия, Германская империя — 16 июня 1977, Александрия, Виргиния, США) — германский, а с 1955 года — американский конструктор ракетно-космической техники, один из основоположников современного ракетостроения, создатель первых баллистических ракет. Член НСДАП с 1937 года, штурмбаннфюрер СС (1943—1945). В США считается отцом американской космической программы.
1876 — русский инженер Павел Яблочков запатентовал электрическую лампочку.
1983 — запущен «Астрон», советский космический ультрафиолетовый телескоп.
2020 — компания Valve выпустила компьютерную игру для устройств виртуальной реальности «Half-Life: Alyx» (на Windows) *Кто не играл - рекомендую
#Biography #Birth_Date #Events #23марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Раздел 8. Stream API и функциональный стиль в Java
Глава 6: Parallel Stream
Гонка параллельных stream в «Библиотеке»
Подготовка: расширение модели Book
Добавьте поле для имитации дорогой операции:
Базовый эксперимент: поиск фантастики
Метод для измерения
Три сценария: малые, средние, большие данные
Сценарий А: 10 элементов, лёгкая операция (complexity = 0)
Ожидаемый результат: Параллельная версия медленнее в 2–10 раз.
Почему:
Разбиение потока на сегменты (splitting)
Передача задач в ForkJoinPool
Синхронизация при сборке результата (ConcurrentHashMap для toList)
Координация потоков дороже, чем сама работа
Сценарий Б: 10 000 элементов, лёгкая операция
Ожидаемый результат: Примерно паритет или лёгкий проигрыш параллельной версии.
Почему:
Накладка на координацию амортизируется
Но операция всё ещё слишком лёгкая — переключение контекста не окупается
Сценарий В: 100 000 элементов, тяжёлая операция (complexity = 1 мс)
Ожидаемый результат: Параллельная версия быстрее в 2–4 раза (на многоядерной машине).
Почему:
Общая работа: 100 000 × 1 мс = 100 секунд последовательно
На 8 ядрах: теоретически ~12.5 секунд + накладка
Накладка амортизируется на большом объёме вычислений
#Java #для_новичков #beginner #stream_api #ForkJoinPool #parallelStream #практика
Глава 6: Parallel Stream
Гонка параллельных stream в «Библиотеке»
Подготовка: расширение модели Book
Добавьте поле для имитации дорогой операции:
public class Book {
private final String title;
private final String author;
private final int year;
private final List<String> genres;
private final int complexity; // «сложность» проверки, влияет на время фильтрации
public Book(String title, String author, int year,
List<String> genres, int complexity) {
this.title = title;
this.author = author;
this.year = year;
this.genres = genres;
this.complexity = complexity;
}
// геттеры...
public boolean isExpensiveFantasyCheck() {
// Имитация дорогой операции: чем выше complexity, тем дольше
try {
Thread.sleep(complexity); // миллисекунды
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return genres.contains("Фантастика");
}
}Базовый эксперимент: поиск фантастики
Метод для измерения
public class ParallelStreamBenchmark {
public static List<String> findFantasyTitlesSequential(List<Book> books) {
return books.stream()
.filter(Book::isExpensiveFantasyCheck)
.map(Book::getTitle)
.collect(Collectors.toList());
}
public static List<String> findFantasyTitlesParallel(List<Book> books) {
return books.parallelStream()
.filter(Book::isExpensiveFantasyCheck)
.map(Book::getTitle)
.collect(Collectors.toList());
}
public static long measure(Runnable task) {
long start = System.nanoTime();
task.run();
long end = System.nanoTime();
return (end - start) / 1_000_000; // миллисекунды
}
}Три сценария: малые, средние, большие данные
Сценарий А: 10 элементов, лёгкая операция (complexity = 0)
List<Book> tinyLibrary = IntStream.range(0, 10)
.mapToObj(i -> new Book("Книга " + i, "Автор " + i,
2000 + i, Arrays.asList(i % 2 == 0 ? "Фантастика" : "Роман"), 0))
.collect(Collectors.toList());
long seqTime = measure(() -> findFantasyTitlesSequential(tinyLibrary));
long parTime = measure(() -> findFantasyTitlesParallel(tinyLibrary));
System.out.println("10 элементов, лёгкая операция:");
System.out.println(" Последовательно: " + seqTime + " мс");
System.out.println(" Параллельно: " + parTime + " мс");
System.out.println(" Накладка: " + (parTime - seqTime) + " мс");
Ожидаемый результат: Параллельная версия медленнее в 2–10 раз.
Почему:
Разбиение потока на сегменты (splitting)
Передача задач в ForkJoinPool
Синхронизация при сборке результата (ConcurrentHashMap для toList)
Координация потоков дороже, чем сама работа
Сценарий Б: 10 000 элементов, лёгкая операция
List<Book> mediumLibrary = IntStream.range(0, 10_000)
.mapToObj(i -> new Book("Книга " + i, "Автор " + i,
2000 + i % 100,
Arrays.asList(i % 3 == 0 ? "Фантастика" : "Роман"), 0))
.collect(Collectors.toList());
// те же измерения
Ожидаемый результат: Примерно паритет или лёгкий проигрыш параллельной версии.
Почему:
Накладка на координацию амортизируется
Но операция всё ещё слишком лёгкая — переключение контекста не окупается
Сценарий В: 100 000 элементов, тяжёлая операция (complexity = 1 мс)
List<Book> largeLibrary = IntStream.range(0, 100_000)
.mapToObj(i -> new Book("Книга " + i, "Автор " + i,
2000 + i % 100,
Arrays.asList(i % 3 == 0 ? "Фантастика" : "Роман"), 1))
.collect(Collectors.toList());
// те же измерения
Ожидаемый результат: Параллельная версия быстрее в 2–4 раза (на многоядерной машине).
Почему:
Общая работа: 100 000 × 1 мс = 100 секунд последовательно
На 8 ядрах: теоретически ~12.5 секунд + накладка
Накладка амортизируется на большом объёме вычислений
#Java #для_новичков #beginner #stream_api #ForkJoinPool #parallelStream #практика
👍3🔥1
Проблема с неправильными источниками
Демонстрация: LinkedList vs ArrayList
Ожидаемый результат: LinkedList в 3–10 раз медленнее из-за невозможности эффективного разбиения.
Интеграция в проект «Библиотеке»
Задача: параллельный анализ большой библиотеки
Добавьте в Library метод для статистики с тяжёлыми вычислениями:
Сравните с последовательной версией на 100 000 книгах.
Практические задания
Задача 1: неправильное использование
Напишите код, который демонстрирует проблемы:
parallelStream().forEach(System.out::println) — беспорядок в выводе
parallelStream().sorted() — избыточная синхронизация
parallelStream() для записи в общий StringBuilder — race condition
Объясните, почему каждый случай ломается.
Задача 2: правильный кастомный коллектор (звёздочка)
Реализуйте parallelStream-совместимый коллектор для Map<Author, List<Book>>, который:
Использует ConcurrentHashMap для аккумулятора
Корректно обрабатывает combiner для слияния частичных результатов
Сохраняет порядок книг внутри автора
Сравните производительность с groupingByConcurrent.
#Java #для_новичков #beginner #stream_api #ForkJoinPool #parallelStream #практика
Демонстрация: LinkedList vs ArrayList
List<Book> arrayList = new ArrayList<>(largeLibrary);
List<Book> linkedList = new LinkedList<>(largeLibrary);
// ArrayList — хорошее разбиение
long arrayListParallel = measure(() ->
arrayList.parallelStream().filter(...).collect(...));
// LinkedList — плохое разбиение
long linkedListParallel = measure(() ->
linkedList.parallelStream().filter(...).collect(...));
System.out.println("ArrayList parallel: " + arrayListParallel + " мс");
System.out.println("LinkedList parallel: " + linkedListParallel + " мс");
Ожидаемый результат: LinkedList в 3–10 раз медленнее из-за невозможности эффективного разбиения.
Интеграция в проект «Библиотеке»
Задача: параллельный анализ большой библиотеки
Добавьте в Library метод для статистики с тяжёлыми вычислениями:
public Map<String, Double> analyzeGenreComplexityParallel() {
return books.parallelStream()
.collect(Collectors.groupingByConcurrent(
book -> book.getGenres().get(0), // основной жанр
Collectors.averagingInt(Book::getComplexity)
));
}Сравните с последовательной версией на 100 000 книгах.
Практические задания
Задача 1: неправильное использование
Напишите код, который демонстрирует проблемы:
parallelStream().forEach(System.out::println) — беспорядок в выводе
parallelStream().sorted() — избыточная синхронизация
parallelStream() для записи в общий StringBuilder — race condition
Объясните, почему каждый случай ломается.
Задача 2: правильный кастомный коллектор (звёздочка)
Реализуйте parallelStream-совместимый коллектор для Map<Author, List<Book>>, который:
Использует ConcurrentHashMap для аккумулятора
Корректно обрабатывает combiner для слияния частичных результатов
Сохраняет порядок книг внутри автора
Сравните производительность с groupingByConcurrent.
#Java #для_новичков #beginner #stream_api #ForkJoinPool #parallelStream #практика
👍4🔥1
Что выведет код?
#Tasks
import java.util.*;
public class Task230326 {
public static void main(String[] args) {
TreeSet<String> set = new TreeSet<>();
set.add("apple");
set.add("banana");
System.out.println(set.contains(null));
}
}
#Tasks
👍2
👍2
Что такое varargs (variable arguments)? 🤓
Ответ:
Varargs (синтаксис ...) позволяет передавать в метод произвольное количество аргументов одного типа.
Объявляется как void method(String... args). Внутри метода аргументы обрабатываются как массив.
Varargs-параметр должен быть последним в списке параметров метода.
Это удобная альтернатива передаче массива — можно вызывать метод как method("a", "b", "c"), так и method() (с пустым массивом).
#собеседование
Ответ:
Объявляется как void method(String... args). Внутри метода аргументы обрабатываются как массив.
Varargs-параметр должен быть последним в списке параметров метода.
Это удобная альтернатива передаче массива — можно вызывать метод как method("a", "b", "c"), так и method() (с пустым массивом).
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4