С 28.03 по 03.04
Предыдущий пост(с 21.03 по 27.03)
Воскресный мотивационный пост:
Не было мотивации, а вообще нужна?) (Признался что только один читает... Напишу ему в личку, наверно)
Запись встреч/видео:
В процессе подготовки
Обучающие статьи:
Раздел 8. Stream API и функциональный стиль
Глава 7: Stream API в экосистеме Java. Интеграционные паттерны
Рефакторинг legacy-кода в проекте «Библиотека»
Глава 8: За пределами коллекций. Бесконечность и I/O
Бесконечные стримы и ленивая генерация
[Совет по Java #020]
Тема: HashSet с изменяемыми объектами ломает контракт.
[Совет по Java #021]
Тема: Использование intern() в (String.intern()) для уникальных строк может забить PermGen/Metaspace и вызвать утечку памяти.
[Совет по Java #022]
Тема: BigDecimal с конструктором от double теряет точность.
Полезные статьи и видео:
Хватит винить кривые руки. Твой язык программирования энергетически тебе не подходит
Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование
Предыдущий пост(с 21.03 по 27.03)
Воскресный мотивационный пост:
Не было мотивации, а вообще нужна?)
Запись встреч/видео:
В процессе подготовки
Обучающие статьи:
Раздел 8. Stream API и функциональный стиль
Глава 7: Stream API в экосистеме Java. Интеграционные паттерны
Рефакторинг legacy-кода в проекте «Библиотека»
Глава 8: За пределами коллекций. Бесконечность и I/O
Бесконечные стримы и ленивая генерация
[Совет по Java #020]
Тема: HashSet с изменяемыми объектами ломает контракт.
[Совет по Java #021]
Тема: Использование intern() в (String.intern()) для уникальных строк может забить PermGen/Metaspace и вызвать утечку памяти.
[Совет по Java #022]
Тема: BigDecimal с конструктором от double теряет точность.
Полезные статьи и видео:
Хватит винить кривые руки. Твой язык программирования энергетически тебе не подходит
Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование
👍2
История технологии сегодня — 5 апреля
ℹ️ Кто родился в этот день
Барон Павел Львович Шиллинг (нем. Schilling von Cannstatt; 5 [16] апреля 1786, Ревель — 25 июля [6 августа] 1837, Санкт-Петербург) — известный российский учёный, член-корреспондент Санкт-Петербургской академии наук, этнограф, филолог, криптограф, изобретатель первого в мире принятого в эксплуатацию электромагнитного телеграфа, автор первого в истории подрыва мины по электрическому проводу, создатель первого в мире телеграфного кода и лучшего в XIX веке секретного шифра, историк-востоковед.
Серге́й Алексе́евич Чаплы́гин (24 марта [5 апреля] 1869, Раненбург, Рязанская губерния — 8 октября 1942, Новосибирск) — русский и советский механик и математик, один из основоположников современной аэромеханики и аэродинамики.
Лоуренс («Ларри») Дейл Белл (5 апреля 1894 — 20 октября 1956) — американский инженер и промышленник, основатель и президент Bell Aircraft: истребитель Аэрокобра, первый в мире самолёт, преодолевший скорость звука, X-1 и самый массовый вертолёт в истории авиации Bell 204 / UH-1.
🌐 Знаковые события
1818 — в Париже барон Карл де Дрез продемонстрировал первое двухколёсное средство передвижения (прообраз велосипеда).
1913 — Нильс Бор завершил статью «О строении атомов и молекул», давшую начало квантовой теории.
1940 — совершён пробный полёт первого отечественного высокоскоростного истребителя И-200.
#Biography #Birth_Date #Events #05апреля
Барон Павел Львович Шиллинг (нем. Schilling von Cannstatt; 5 [16] апреля 1786, Ревель — 25 июля [6 августа] 1837, Санкт-Петербург) — известный российский учёный, член-корреспондент Санкт-Петербургской академии наук, этнограф, филолог, криптограф, изобретатель первого в мире принятого в эксплуатацию электромагнитного телеграфа, автор первого в истории подрыва мины по электрическому проводу, создатель первого в мире телеграфного кода и лучшего в XIX веке секретного шифра, историк-востоковед.
Серге́й Алексе́евич Чаплы́гин (24 марта [5 апреля] 1869, Раненбург, Рязанская губерния — 8 октября 1942, Новосибирск) — русский и советский механик и математик, один из основоположников современной аэромеханики и аэродинамики.
Лоуренс («Ларри») Дейл Белл (5 апреля 1894 — 20 октября 1956) — американский инженер и промышленник, основатель и президент Bell Aircraft: истребитель Аэрокобра, первый в мире самолёт, преодолевший скорость звука, X-1 и самый массовый вертолёт в истории авиации Bell 204 / UH-1.
1818 — в Париже барон Карл де Дрез продемонстрировал первое двухколёсное средство передвижения (прообраз велосипеда).
1913 — Нильс Бор завершил статью «О строении атомов и молекул», давшую начало квантовой теории.
1940 — совершён пробный полёт первого отечественного высокоскоростного истребителя И-200.
#Biography #Birth_Date #Events #05апреля
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
10. Timeout и Bulkhead: как не дать сервису утонуть в запросах?
Retry и Circuit Breaker не спасут, если сервис просто начинает тормозить.
В этом видео разбираем два недооценённых, но критически важных паттерна отказоустойчивости — Timeout и Bulkhead — и смотрим, как они работают в реальном production-сценарии.
На примере OrderHub (микросервис на Spring Boot + Resilience4j) рассказываю:
🔵 почему один медленный сервис может убить всю систему;
🔵 как Thread Starvation за секунды забивает пул потоков Tomcat;
🔵 как настроить Timeout, чтобы не ждать ответ вечно;
🔵 как Bulkhead изолирует ресурсы и не даёт одному «тормозу» обрушить всё приложение;
🔵 где и почему Retry не работает, а Bulkhead спасает;
Исходный код проекта на GitHub очень ждет Ваших звезд☺️ (Вам че блин, жалко?)
Ссылка на Youtube
Ссылка на Рутьюб
Смотрите, ставьте лайки, подписывайтесь на каналы!✌️
❗️ ❗️ ❗️ Огромная просьба - если Вам понравилась моя работа, распространите эту серию по всем доступным вам местам: телеграм, discord и прочим каналам. ❗️ ❗️ ❗️
Буду крайне благодарен🙂
Retry и Circuit Breaker не спасут, если сервис просто начинает тормозить.
В этом видео разбираем два недооценённых, но критически важных паттерна отказоустойчивости — Timeout и Bulkhead — и смотрим, как они работают в реальном production-сценарии.
На примере OrderHub (микросервис на Spring Boot + Resilience4j) рассказываю:
Исходный код проекта на GitHub очень ждет Ваших звезд
Ссылка на Youtube
Ссылка на Рутьюб
Смотрите, ставьте лайки, подписывайтесь на каналы!
Буду крайне благодарен
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
История технологии сегодня — 6 апреля
ℹ️ Кто родился в этот день
Антон Герман Герард (Энтони) Фо́ккер (нид. Anton Herman Gerard «Anthony» Fokker; 6 апреля 1890 года, Кедири, Голландская Ост-Индия — 23 декабря 1939 года, Нью-Йорк, США) — нидерландский авиаконструктор. Первым построил четырёхмоторный пассажирский самолёт — F-32 (1929 год), который мог перевозить 32 пассажира. В конце 1920-х годов «Фоккеры» составляли свыше 40 % самолётного парка американских авиакомпаний.
🌐 Знаковые события
1965 — в США запущен первый в мире коммерческий спутник связи «Ранняя Пташка», работающий на геостационарной орбите.
1973 — запуск американской автоматической межпланетной станции «Пионер-11» в сторону планеты Сатурн.
#Biography #Birth_Date #Events #06апреля
Антон Герман Герард (Энтони) Фо́ккер (нид. Anton Herman Gerard «Anthony» Fokker; 6 апреля 1890 года, Кедири, Голландская Ост-Индия — 23 декабря 1939 года, Нью-Йорк, США) — нидерландский авиаконструктор. Первым построил четырёхмоторный пассажирский самолёт — F-32 (1929 год), который мог перевозить 32 пассажира. В конце 1920-х годов «Фоккеры» составляли свыше 40 % самолётного парка американских авиакомпаний.
1965 — в США запущен первый в мире коммерческий спутник связи «Ранняя Пташка», работающий на геостационарной орбите.
1973 — запуск американской автоматической межпланетной станции «Пионер-11» в сторону планеты Сатурн.
#Biography #Birth_Date #Events #06апреля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Раздел 8. Stream API и функциональный стиль в Java
Глава 8: За пределами коллекций. Бесконечность и I/O
I/O операции как потоки данных
Stream API расширяет свою абстракцию от коллекций в памяти к внешним источникам данных. Файлы, сетевые соединения, процессы — всё это может быть представлено как Stream<String> или Stream<Byte>, с ленивой подкачкой данных по мере необходимости. Этот подход критичен для обработки данных, не помещающихся в RAM: гигабайтные логи, бесконечные потоки событий, построчная обработка без полной загрузки.
Files.lines: файловый поток строк
Метод Files.lines(Path path) — фабрика потоков для текстовых файлов. Он возвращает Stream<String>, где каждый элемент — строка файла, декодированная в указанной или платформенной кодировке (по умолчанию UTF-8).
Критически важно: Files.lines возвращает поток, реализующий AutoCloseable. Файловый дескриптор открывается при создании потока и должен быть явно закрыт. Без try-with-resources или явного close() ресурс утекает до сборки мусора, что при высокой частоте операций приводит к исчерпанию дескрипторов ОС (Too many open files).
Паттерн обработки больших файлов:
Здесь ни одна строка не хранится в памяти целиком. Files.lines использует BufferedReader с буфером 8192 байт, читая файл блоками и выдавая строки по границам \n или \r\n. Обработка происходит пакетно, с постоянным потреблением памяти независимо от размера файла.
BufferedReader.lines: низкоуровневый контроль
Для специализированных сценариев — настройка размера буфера, обработка кодировок, работа с существующим Reader:
BufferedReader.lines() возвращает поток с теми же семантиками ленивости, но без привязки к Path — работает с любым Reader, включая StringReader, CharArrayReader, сетевые потоки через InputStreamReader.
#Java #для_новичков #beginner #stream_api
Глава 8: За пределами коллекций. Бесконечность и I/O
I/O операции как потоки данных
Stream API расширяет свою абстракцию от коллекций в памяти к внешним источникам данных. Файлы, сетевые соединения, процессы — всё это может быть представлено как Stream<String> или Stream<Byte>, с ленивой подкачкой данных по мере необходимости. Этот подход критичен для обработки данных, не помещающихся в RAM: гигабайтные логи, бесконечные потоки событий, построчная обработка без полной загрузки.
Files.lines: файловый поток строк
Метод Files.lines(Path path) — фабрика потоков для текстовых файлов. Он возвращает Stream<String>, где каждый элемент — строка файла, декодированная в указанной или платформенной кодировке (по умолчанию UTF-8).
// Базовое использование с автоматическим закрытием
try (Stream<String> lines = Files.lines(Path.of("access.log"))) {
long errorCount = lines
.filter(line -> line.contains("ERROR"))
.count();
}
Критически важно: Files.lines возвращает поток, реализующий AutoCloseable. Файловый дескриптор открывается при создании потока и должен быть явно закрыт. Без try-with-resources или явного close() ресурс утекает до сборки мусора, что при высокой частоте операций приводит к исчерпанию дескрипторов ОС (Too many open files).
Паттерн обработки больших файлов:
// Фильтрация, трансформация, запись результата — всё в потоковом режиме
try (Stream<String> lines = Files.lines(Path.of("huge_input.txt"));
BufferedWriter writer = Files.newBufferedWriter(Path.of("filtered_output.txt"))) {
lines.parallel() // Осторожно: см. предупреждение ниже
.filter(line -> line.length() > 100)
.map(String::toUpperCase)
.map(line -> line + System.lineSeparator())
.forEachOrdered(line -> {
try {
writer.write(line);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
}
Здесь ни одна строка не хранится в памяти целиком. Files.lines использует BufferedReader с буфером 8192 байт, читая файл блоками и выдавая строки по границам \n или \r\n. Обработка происходит пакетно, с постоянным потреблением памяти независимо от размера файла.
BufferedReader.lines: низкоуровневый контроль
Для специализированных сценариев — настройка размера буфера, обработка кодировок, работа с существующим Reader:
// Кастомная кодировка и буфер
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream("legacy.txt"),
StandardCharsets.ISO_8859_1
),
16384 // Удвоенный буфер для последовательного чтения
);
Stream<String> lines = reader.lines()) {
lines.map(this::parseLegacyRecord)
.filter(Objects::nonNull)
.forEach(this::processRecord);
}
BufferedReader.lines() возвращает поток с теми же семантиками ленивости, но без привязки к Path — работает с любым Reader, включая StringReader, CharArrayReader, сетевые потоки через InputStreamReader.
#Java #для_новичков #beginner #stream_api
👍5
Управление ресурсами и исключения
I/O потоки добавляют сложность обработки checked исключений. Лямбды в Stream API не объявляют throws, требуя обёртки:
Решения:
Обёртка в runtime exception:
Извлечение в метод с обёрткой:
Специализированный коллектор для ошибок (см. Урок 3.3): разделение успешных результатов и ошибок без прерывания потока.
Параллелизм и I/O: катастрофическая комбинация
Предупреждение из предыдущих глав приобретает критическую важность для I/O. parallelStream() на Files.lines или BufferedReader.lines() — антипаттерн с тяжёлыми последствиями:
Проблемы:
Нет разделения: BufferedReader не поддерживает trySplit(). lines().parallel() не делит файл на сегменты — он создаёт иллюзию параллелизма, фактически синхронизируя доступ к общему Reader.
Блокировка common pool: если map содержит блокирующие операции (запросы к БД, HTTP вызовы, Thread.sleep), все воркеры ForkJoinPool.commonPool() замораживаются. Другие компоненты приложения (CompletableFuture, другие parallelStream) парализуются.
Нарушение порядка: forEach в параллельном потоке не сохраняет порядок строк файла. forEachOrdered требует синхронизации, сводя на нет выигрыш.
Исключение: если обработка строк CPU-bound и дорога (сложный парсинг, криптографические операции), а чтение — отдельная стадия, можно разделить:
Но лучший подход для I/O-bound задач — CompletableFuture с кастомным пулом или virtual threads (Java 21+):
#Java #для_новичков #beginner #stream_api
I/O потоки добавляют сложность обработки checked исключений. Лямбды в Stream API не объявляют throws, требуя обёртки:
// Проблема: readLine бросает IOException
Stream<String> lines = Files.lines(path);
lines.map(line -> {
// Ошибка компиляции: unreported exception IOException
return expensiveParser.parse(line);
});
Решения:
Обёртка в runtime exception:
.lines.map(line -> {
try {
return expensiveParser.parse(line);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
})Извлечение в метод с обёрткой:
private ParsedRecord safeParse(String line) {
try {
return expensiveParser.parse(line);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
// В потоке
.lines.map(this::safeParse)Специализированный коллектор для ошибок (см. Урок 3.3): разделение успешных результатов и ошибок без прерывания потока.
Параллелизм и I/O: катастрофическая комбинация
Предупреждение из предыдущих глав приобретает критическую важность для I/O. parallelStream() на Files.lines или BufferedReader.lines() — антипаттерн с тяжёлыми последствиями:
// КАТАСТРОФА: параллельное чтение файла через common pool
try (Stream<String> lines = Files.lines(Path.of("access.log"))) {
lines.parallel() // Нет выигрыша, есть риск
.map(this::blockingDatabaseLookup) // Блокировка воркера
.collect(toList());
}
Проблемы:
Нет разделения: BufferedReader не поддерживает trySplit(). lines().parallel() не делит файл на сегменты — он создаёт иллюзию параллелизма, фактически синхронизируя доступ к общему Reader.
Блокировка common pool: если map содержит блокирующие операции (запросы к БД, HTTP вызовы, Thread.sleep), все воркеры ForkJoinPool.commonPool() замораживаются. Другие компоненты приложения (CompletableFuture, другие parallelStream) парализуются.
Нарушение порядка: forEach в параллельном потоке не сохраняет порядок строк файла. forEachOrdered требует синхронизации, сводя на нет выигрыш.
Исключение: если обработка строк CPU-bound и дорога (сложный парсинг, криптографические операции), а чтение — отдельная стадия, можно разделить:
// Чтение последовательное, обработка параллельная — но с осторожностью
List<String> batch = new ArrayList<>(BATCH_SIZE);
try (Stream<String> lines = Files.lines(Path.of("huge.txt"))) {
lines.forEach(line -> {
batch.add(line);
if (batch.size() >= BATCH_SIZE) {
processBatchParallel(new ArrayList<>(batch)); // Копия для безопасности
batch.clear();
}
});
if (!batch.isEmpty()) processBatchParallel(batch);
}
private void processBatchParallel(List<String> batch) {
batch.parallelStream()
.map(this::expensiveCpuBoundTransform)
.collect(toList()); // Результат куда-то сохраняется
}
Но лучший подход для I/O-bound задач — CompletableFuture с кастомным пулом или virtual threads (Java 21+):
// Правильно: изолированный пул для блокирующих операций
ExecutorService ioPool = Executors.newFixedThreadPool(50);
try (Stream<String> lines = Files.lines(Path.of("urls.txt"))) {
List<CompletableFuture<Response>> futures = lines
.map(url -> CompletableFuture.supplyAsync(
() -> fetchHttp(url),
ioPool
))
.collect(toList());
List<Response> results = futures.stream()
.map(CompletableFuture::join)
.collect(toList());
} finally {
ioPool.shutdown();
}
#Java #для_новичков #beginner #stream_api
👍4
Паттерн: конвейер ETL без промежуточных файлов
Классический паттерн Extract-Transform-Load реализуется через композицию потоков:
Память потребляется постоянно (размер буфера чтения + одна строка обработки), независимо от размера входного файла. Скорость ограничена I/O диска или сети, а не CPU.
Закрытие и обработка ошибок
При исключении в промежуточной операции поток прерывается, но ресурс в try-with-resources закрывается корректно:
Но если исключение происходит в терминальной операции, а промежуточные содержат ресурсы (например, map открывает соединения), требуется явное управление:
#Java #для_новичков #beginner #stream_api
Классический паттерн Extract-Transform-Load реализуется через композицию потоков:
// Извлечение из CSV
try (Stream<String> lines = Files.lines(Path.of("input.csv"));
// Загрузка в выходной файл
BufferedWriter writer = Files.newBufferedWriter(Path.of("output.json"))) {
lines.skip(1) // Пропуск заголовка
.map(this::parseCsvLine) // String -> Record
.filter(Objects::nonNull) // Удаление malformed
.map(this::transformToJson) // Record -> JSON string
.forEach(json -> {
try {
writer.write(json);
writer.newLine();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
}
Память потребляется постоянно (размер буфера чтения + одна строка обработки), независимо от размера входного файла. Скорость ограничена I/O диска или сети, а не CPU.
Закрытие и обработка ошибок
При исключении в промежуточной операции поток прерывается, но ресурс в try-with-resources закрывается корректно:
try (Stream<String> lines = Files.lines(Path.of("corrupt.txt"))) {
lines.map(this::parse)
.filter(Objects::nonNull)
.forEach(this::process);
// Если parse бросает RuntimeException на 1000-й строке,
// lines.close() вызывается автоматически
}Но если исключение происходит в терминальной операции, а промежуточные содержат ресурсы (например, map открывает соединения), требуется явное управление:
// Антипаттерн: ресурс внутри map
lines.map(line -> {
Connection conn = pool.borrow(); // Открытие здесь
return query(conn, line); // Если исключение, conn не возвращается
})
// Правильно: try-with-resources внутри лямбды, или вне потока
#Java #для_новичков #beginner #stream_api
👍4
Что выведет код?
#Tasks
import java.io.*;
public class Task060426 {
public static void main(String[] args) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(255);
dos.flush();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
DataInputStream dis = new DataInputStream(bais);
int value = dis.read();
System.out.println(value);
}
}
#Tasks
👍1
В чем разница между композицией и наследованием? 🤓
Ответ:
Оба принципа используются для переиспользования кода.
Наследование — это отношение "is-a" (например, Собака — это Животное). Класс-потомок получает все публичные и защищенные поля и методы родителя. Но оно создает жесткую связь и нарушает инкапсуляцию, если неосторожно.
Композиция — отношение "has-a" (например, Машина имеет Двигатель). Объект одного класса содержит ссылку на объект другого. Это гибче, слабее связанность, легче тестировать и изменять.
Принцип: "Предпочитайте композицию наследованию" (Favor composition over inheritance).
#собеседование
Ответ:
Наследование — это отношение "is-a" (например, Собака — это Животное). Класс-потомок получает все публичные и защищенные поля и методы родителя. Но оно создает жесткую связь и нарушает инкапсуляцию, если неосторожно.
Композиция — отношение "has-a" (например, Машина имеет Двигатель). Объект одного класса содержит ссылку на объект другого. Это гибче, слабее связанность, легче тестировать и изменять.
Принцип: "Предпочитайте композицию наследованию" (Favor composition over inheritance).
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥2
История технологии сегодня — 7 апреля
ℹ️ Кто родился в этот день
Леони́д Вениами́нович Ке́лдыш (7 апреля 1931, Москва, РСФСР — 11 ноября 2016, Москва) — советский и российский физик-теоретик, академик РАН (академик АН СССР с 1976), доктор физико-математических наук (1965), профессор. Совместно с Ю. В. Копаевым предложил известную модель фазового перехода металл-полупроводник, известную как «экситонный диэлектрик». В 1968 году вместе с другим своим учеником А. Н. Козловым предсказал бозе-эйнштейновскую конденсацию экситонов, а также показал, что неравновесные экситоны в сильно возбуждённом полупроводнике должны формировать электронно-дырочные капли. В ряде работ Л. В. Келдыш исследовал явления, связанные с глубоко лежащими уровнями в полупроводниках, ударной ионизацией, «фононным ветром» и т.д.
🌐 Знаковые события
1964 — IBM объявляет о рождении легендарной System/360. Мейнфреймы этого типа будут долго лидировать на рынке и составлять основу компьютерного парка большинства стран мира. В СССР известны как системы с маркой ЕС ЭВМ.
1994 — в международной базе данных национальных доменов верхнего уровня появилась запись о домене .ru.
#Biography #Birth_Date #Events #07апреля
Леони́д Вениами́нович Ке́лдыш (7 апреля 1931, Москва, РСФСР — 11 ноября 2016, Москва) — советский и российский физик-теоретик, академик РАН (академик АН СССР с 1976), доктор физико-математических наук (1965), профессор. Совместно с Ю. В. Копаевым предложил известную модель фазового перехода металл-полупроводник, известную как «экситонный диэлектрик». В 1968 году вместе с другим своим учеником А. Н. Козловым предсказал бозе-эйнштейновскую конденсацию экситонов, а также показал, что неравновесные экситоны в сильно возбуждённом полупроводнике должны формировать электронно-дырочные капли. В ряде работ Л. В. Келдыш исследовал явления, связанные с глубоко лежащими уровнями в полупроводниках, ударной ионизацией, «фононным ветром» и т.д.
1964 — IBM объявляет о рождении легендарной System/360. Мейнфреймы этого типа будут долго лидировать на рынке и составлять основу компьютерного парка большинства стран мира. В СССР известны как системы с маркой ЕС ЭВМ.
1994 — в международной базе данных национальных доменов верхнего уровня появилась запись о домене .ru.
#Biography #Birth_Date #Events #07апреля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
[Совет по Java #023]
Тема: Тайм-ауты в параллельных стримах нельзя контролировать.
Проблема: Параллельные стримы (parallelStream()) по умолчанию используют общий пул ForkJoinPool.commonPool(), который разделяется между всеми параллельными операциями в приложении. У этого пула нет механизма принудительной остановки долго выполняющихся задач. Если внутри операции стрима возникает блокировка (например, медленный внешний вызов, бесконечный цикл или зависание), то поток, захваченный этой задачей, блокируется на неопределенное время.
Стандартные средства CompletableFuture или ExecutorService позволяют установить тайм-аут через get(timeout, TimeUnit), но для parallelStream такой возможности нет. В результате приложение может зависнуть полностью, так как общий пул истощается, и другие параллельные операции не могут выполняться. Даже если запустить задачу в отдельном потоке с тайм-аутом, прерывание через Thread.interrupt() не гарантирует остановки, если код внутри стрима не проверяет флаг прерывания.
Решение: Избегайте использования parallelStream() для операций, которые могут зависнуть или выполняться долго и неопределенно.
Вместо этого используйте явный ExecutorService с фиксированным пулом потоков, где можно контролировать тайм-ауты через Future.get(timeout, TimeUnit). Если необходимо обрабатывать большие объемы данных параллельно, разбейте коллекцию вручную и отправьте задачи в пул.
Для асинхронной обработки используйте CompletableFuture с orTimeout() (Java 9+) и completeOnTimeout(). В крайнем случае, можно создать собственный ForkJoinPool для изоляции операций, но это не решает проблему прерывания.
Тема: Тайм-ауты в параллельных стримах нельзя контролировать.
Проблема: Параллельные стримы (parallelStream()) по умолчанию используют общий пул ForkJoinPool.commonPool(), который разделяется между всеми параллельными операциями в приложении. У этого пула нет механизма принудительной остановки долго выполняющихся задач. Если внутри операции стрима возникает блокировка (например, медленный внешний вызов, бесконечный цикл или зависание), то поток, захваченный этой задачей, блокируется на неопределенное время.
Стандартные средства CompletableFuture или ExecutorService позволяют установить тайм-аут через get(timeout, TimeUnit), но для parallelStream такой возможности нет. В результате приложение может зависнуть полностью, так как общий пул истощается, и другие параллельные операции не могут выполняться. Даже если запустить задачу в отдельном потоке с тайм-аутом, прерывание через Thread.interrupt() не гарантирует остановки, если код внутри стрима не проверяет флаг прерывания.
Решение: Избегайте использования parallelStream() для операций, которые могут зависнуть или выполняться долго и неопределенно.
Вместо этого используйте явный ExecutorService с фиксированным пулом потоков, где можно контролировать тайм-ауты через Future.get(timeout, TimeUnit). Если необходимо обрабатывать большие объемы данных параллельно, разбейте коллекцию вручную и отправьте задачи в пул.
Для асинхронной обработки используйте CompletableFuture с orTimeout() (Java 9+) и completeOnTimeout(). В крайнем случае, можно создать собственный ForkJoinPool для изоляции операций, но это не решает проблему прерывания.
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
public class ParallelStreamTimeout {
//Антипаттерн: parallelStream без контроля тайм-аута
public static List<Integer> dangerousParallel() {
List<Integer> numbers = IntStream.range(0, 10).boxed().collect(Collectors.toList());
return numbers.parallelStream()
.map(n -> {
// Представьте, что здесь долгий внешний вызов
try { Thread.sleep(100_000); } catch (InterruptedException e) {}
return n * 2;
})
.collect(Collectors.toList());
// Зависнет навсегда, общий pool заблокирован
}
//Решение: явный ExecutorService с тайм-аутом
public static List<Integer> safeWithExecutor(List<Integer> numbers) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(4);
List<Future<Integer>> futures = numbers.stream()
.map(n -> executor.submit(() -> {
// Симуляция долгой операции
Thread.sleep(100); // реальная логика
return n * 2;
}))
.collect(Collectors.toList());
List<Integer> result = new ArrayList<>();
for (Future<Integer> future : futures) {
try {
// Тайм-аут 1 секунда на задачу
result.add(future.get(1, TimeUnit.SECONDS));
} catch (TimeoutException e) {
future.cancel(true); // Попытка прервать
result.add(null); // или обработка ошибки
System.err.println("Задача не уложилась в тайм-аут");
} catch (ExecutionException e) {
throw new RuntimeException(e.getCause());
}
}
executor.shutdown();
return result;
}
//Альтернатива: CompletableFuture с тайм-аутом (Java 9+)
public static CompletableFuture<List<Integer>> asyncWithTimeout(List<Integer> numbers) {
List<CompletableFuture<Integer>> futures = numbers.stream()
.map(n -> CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(100); } catch (InterruptedException e) {}
return n * 2;
}).orTimeout(500, TimeUnit.MILLISECONDS) // Тайм-аут 500 мс
.exceptionally(ex -> {
System.err.println("Ошибка или тайм-аут: " + ex.getMessage());
👍4