Java for Beginner
870 subscribers
1.01K photos
275 videos
14 files
1.69K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Вы соскучились по встречам?
Anonymous Poll
56%
Да!
4%
Нет!
0%
Похер
36%
Встреча с кем?
4%
Что я тут делаю?
👍1
История технологий сегодня — 16 марта

ℹ️ Кто родился в этот день

Ричард Мэттью Столлман (англ. Richard Matthew Stallman; род. 16 марта 1953, Манхэттен, Нью-Йорк), также упоминаемый как rms, — основатель движения свободного программного обеспечения, проекта GNU, Фонда свободного программного обеспечения и Лиги за свободу программирования. Автор концепции «копилефта», призванной защищать идеалы движения; эту концепцию он с помощью юристов позже воплотил в лицензии GNU General Public License (GNU GPL) для ПО. Ранее также известный программист. Из авторских программ можно отметить GNU Emacs, Коллекция компиляторов GNU (GCC) и Отладчик GNU (GDB). С середины 1990-х годов Столлман стал программировать значительно меньше, посвятив себя распространению идей свободного ПО.

Э́ндрю Стюарт Таненба́ум (англ. Andrew Stuart Tanenbaum; род. 16 марта 1944, Нью-Йорк, Нью-Йорк), иногда упоминаемый под псевдонимом AST или Papá Tanenbaumголландский учёный-компьютерщик американского происхождения, почётный профессор компьютерных наук в Амстердамском свободном университете в Нидерландах. Известен как автор MINIX (свободная Unix-подобная операционная система для учебных целей) и RFID-вируса. Автор книг по информатике, которые считаются стандартами в данной области. Сам он считает свою преподавательскую деятельность наиболее важной. Является главным разработчиком пакета «Amsterdam Compiler Kit».

Фре́дерик Ра́йнес (англ. Frederick Reines; 16 марта 1918, Патерсон, штат Нью-Джерси, США — 26 августа 1998, Ориндж, штат Калифорния, США)американский физик, профессор, лауреат Нобелевской премии по физике (1995) за открытие нейтрино.


🌐 Знаковые события

1936 — с конвейера Горьковского автомобильного завода сошла первая советская легковая автомашина — лимузин марки «М-1».


#Biography #Birth_Date #Events #16марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Раздел 8. Stream API и функциональный стиль в Java

Глава 5: Контракты. Почему equals, hashCode и иммутабельность — закон

(Практика): Саботаж и починка в «Библиотеке»

Создание «сломанного» класса Book

Создайте или обновите класс Book с минимальными полями:
public class Book {
private final String title;
private final String author;

public Book(String title, String author) {
this.title = Objects.requireNonNull(title, "title не может быть null");
this.author = Objects.requireNonNull(author, "author не может быть null");
}

public String getTitle() { return title; }
public String getAuthor() { return author; }

@Override
public String toString() {
return "Book[title=" + title + ", author=" + author + "]";
}
}


Частичная реализация equals (только по title)

Добавьте equals, сравнивающий только название:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Book)) return false;
Book book = (Book) o;
return title.equals(book.title); // Только title! Автор игнорируется.
}


Намеренно не добавляйте hashCode!

Демонстрация проблемы в Stream API

Проблема 1: distinct() «слипает» разные книги

Создайте тестовый метод в Library или Main:
public void demonstrateDistinctProblem() {
List<Book> books = Arrays.asList(
new Book("Война и мир", "Толстой"),
new Book("Война и мир", "Достоевский"), // Другой автор!
new Book("1984", "Оруэлл")
);

List<Book> distinct = books.stream()
.distinct()
.collect(Collectors.toList());

System.out.println("Исходный список: " + books.size() + " книг");
System.out.println("После distinct: " + distinct.size() + " книг");
System.out.println("Уникальные книги: " + distinct);
}


Ожидаемый вывод:
Исходный список: 3 книг
После distinct: 2 книг
Уникальные книги: [Book[title=Война и мир, author=Толстой], Book[title=1984, author=Оруэлл]]


Что произошло: distinct() использует hashCode и equals. Поскольку hashCode не переопределён, наследуется от Object — разные адреса памяти → разные хэши. Но equals говорит, что книги равны по названию.
На самом деле поведение непредсказуемо: distinct() внутри использует HashSet, который сначала сравнивает hashCode, потом equals. Если хэши разные (а они будут разные), элементы попадают в разные бакеты и никогда не сравниваются через equals. Толстой и Достоевский останутся оба!

Проверьте: Запустите код. Если обе «Войны и мира» остались — hashCode разные спасает. Если одна пропала — вы попали в коллизию (редко, но возможно).

Проблема 2: toMap и потеря значений

Создайте Map книга → количество на складе:
public void demonstrateToMapProblem() {
List<Book> inventory = Arrays.asList(
new Book("Война и мир", "Толстой"),
new Book("Война и мир", "Достоевский")
);

Map<Book, Integer> stock = inventory.stream()
.collect(Collectors.toMap(
Function.identity(),
b -> 10, // количество на складе
(a, b) -> a + b // слияние при коллизии
));

System.out.println("Map: " + stock);

// Попытка получить значение по «эквивалентной» книге
Book lookup = new Book("Война и мир", "Толстой");
System.out.println("Поиск " + lookup + ": " + stock.get(lookup));

Book lookupWrongAuthor = new Book("Война и мир", "Гоголь");
System.out.println("Поиск " + lookupWrongAuthor + ": " + stock.get(lookupWrongAuthor));
}


Анализ:
toMap использует hashCode для выбора бакета, equals для проверки существования ключа
Без переопределённого hashCode — разные объекты, разные бакеты
Даже если equals сказал бы «равны», HashMap не найдёт элемент из-за разных хэшей
lookup и lookupWrongAuthor оба вернут null или случайное значение
👍5
Проблема 3: groupingBy ломается
public void demonstrateGroupingProblem() {
List<Book> books = Arrays.asList(
new Book("Война и мир", "Толстой"),
new Book("Война и мир", "Достоевский"),
new Book("Война и мир", "Тургенев")
);

Map<Book, List<Book>> byBook = books.stream()
.collect(Collectors.groupingBy(Function.identity()));

System.out.println("Групп: " + byBook.size());
byBook.forEach((k, v) -> System.out.println(k + " -> " + v.size()));
}


Ожидаемо: 3 группы (все разные авторы) или 1 группа (если equals/hashCode считают их одинаковыми).
Фактически: Непредсказуемо из-за отсутствующего hashCode.


Исправление: корректные equals и hashCode

Правильная реализация по всем полям

Обновите класс Book:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Book)) return false;
Book book = (Book) o;
return title.equals(book.title) &&
author.equals(book.author); // Теперь оба поля!
}

@Override
public int hashCode() {
return Objects.hash(title, author); // Консистентно с equals
}


Контракт equals и hashCode:
Рефлексивность: x.equals(x) всегда true
Симметричность: x.equals(y) ⇔ y.equals(x)
Транзитивность: x.equals(y) и y.equals(z) → x.equals(z)
Консистентность: повторные вызовы дают тот же результат
С null: x.equals(null) всегда false
Критически: Если x.equals(y), то x.hashCode() == y.hashCode()

Проверка исправления

Повторите все демонстрации
public void demonstrateFixed() {
List<Book> books = Arrays.asList(
new Book("Война и мир", "Толстой"),
new Book("Война и мир", "Достоевский"),
new Book("1984", "Оруэлл")
);

// distinct() — теперь 3 уникальные книги
System.out.println("distinct: " + books.stream().distinct().count());

// toMap — теперь 3 записи
Map<Book, Integer> stock = books.stream()
.collect(Collectors.toMap(
Function.identity(),
b -> 10,
Integer::sum
));
System.out.println("Map size: " + stock.size());

// Поиск работает
Book lookup = new Book("Война и мир", "Толстой");
System.out.println("Найдено: " + stock.get(lookup));
}


Ожидаемый результат: 3 уникальных книги, Map с 3 записями, поиск находит значение.


#Java #для_новичков #beginner #stream_api #практика
👍4
Альтернативные стратегии equals/hashCode

Стратегия 1: Только неизменяемый идентификатор (ISBN)

Если Book имеет уникальный ISBN:
private final String isbn;  // уникальный, неизменяемый

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Book)) return false;
return isbn.equals(((Book) o).isbn);
}

@Override
public int hashCode() {
return isbn.hashCode();
}


Плюсы: Эффективно, надёжно. Минусы: Требует уникального поля.

Стратегия 2: Все поля

Плюсы: Корректно для value objects. Минусы: Медленнее, чувствительно к изменениям.

Стратегия 3: Не переопределять (только identity)

Если Book — entity с уникальным ID в базе данных, и вы никогда не сравниваете разные объекты с одинаковым ID:
// Нет equals/hashCode — используем Object

Плюсы: Просто. Минусы: distinct(), toMap, groupingBy работают по ссылке, не по содержимому.

Практические задания

Задача 1: воспроизвести все три проблемы

В проекте «Библиотека» создайте класс BrokenBook с equals только по title и без hashCode.

Продемонстрируйте:
distinct() «проглатывает» книги или нет (зависит от хэш-коллизий)
toMap создаёт больше записей, чем ожидается
groupingBy разбивает на неожиданное количество групп
Зафиксируйте наблюдения в комментариях.

Задача 2: исправить и сравнить

Создайте FixedBook с корректной парой equals/hashCode. Повторите те же операции.

Убедитесь, что:
Толстой, Достоевский, Тургенев — три разные книги с одинаковым названием
distinct() оставляет все три
toMap создаёт три записи
Поиск по ключу находит правильное значение

Задача 3: специфический equals для бизнес-логики

Добавьте в Library метод findByTitle(String title), который находит все книги с данным названием (независимо от автора).
Реализуйте через filter с кастомным предикатом, не через equals:
public List<Book> findByTitle(String title) {
return books.stream()
.filter(b -> b.getTitle().equals(title))
.collect(Collectors.toList());
}


Объясните, почему это безопаснее, чем менять equals для всего класса.

Задача 4: кеширование hashCode (звёздочка)


Если поля title и author неизменяемы, закешируйте hashCode:
private int hashCode;  // 0 = не вычислено

@Override
public int hashCode() {
int result = hashCode;
if (result == 0) {
result = Objects.hash(title, author);
hashCode = result;
}
return result;
}


Сравните производительность при частом использовании в HashMap для миллиона операций.

#Java #для_новичков #beginner #stream_api #практика
👍5
Что выведет код?

public class Task160326 {
public static void main(String[] args) {
String s1 = " hello ".trim();
String s2 = " hello ".strip();
String s3 = "\u2000hello\u2000".trim();
String s4 = "\u2000hello\u2000".strip();

System.out.println(s1.equals(s2));
System.out.println(s3.length());
System.out.println(s4.length());
}
}


#Tasks
👍2
Варианты ответа:
Anonymous Quiz
27%
true 5 5
13%
false 7 5
60%
true 7 5
0%
false 5 5
👍2
Что такое Profiling и для чего он нужен? 🤓

Ответ:

Profiling
— это процесс динамического анализа работающего приложения для измерения его производительности, использования памяти и поведения потоков.

Профилировщики (например, JProfiler, VisualVM, YourKit) собирают данные о:
CPU Profiling — какие методы потребляют больше всего процессорного времени;
Memory Profiling — какие объекты занимают память, поиск утечек памяти;
Thread Profiling — состояние потоков, поиск deadlock'ов.

Это незаменимый инструмент для оптимизации производительности и диагностики проблем в сложных приложениях.


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
История технологий сегодня — 17 марта

ℹ️ Кто родился в этот день

Влади́мир Па́влович Барми́н (4 (17) марта 1909 года, Москва — 17 июля 1993 года, там же) советский учёный, конструктор реактивных пусковых установок, ракетно-космических и боевых стартовых комплексов. Один из основоположников советской космонавтики. С 1947 года под руководством Бармина были разработаны стартовые комплексы для многих ракет конструкции Королёва: Р-1, Р-2, Р-11, Р-5, Р-5М — первой стратегической ракеты с ядерным боезарядом Р-5М. В 1957 году завершены работы над стартовым комплексом первой в мире межконтинентальной баллистической ракеты Р-7, которая вывела на орбиту Земли первый искусственный спутник Земли и первого космонавта Юрия Гагарина.

Готтлиб Вильгельм Даймлер (нем. Gottlieb Wilhelm Daimler); собственно Доймлер (Däumler; 17 марта 1834, Шорндорф, Королевство Вюртемберг, Германский союз — 6 марта 1900, Бад-Канштатт, Королевство Вюртемберг, Германская империя) — немецкий инженер, конструктор и промышленник. Совместно с Вильгельмом Майбахом Даймлер разработал один из первых автомобилей и несколько типов бензиновых двигателей внутреннего сгорания.


🌐 Знаковые события

1966 — первый пуск с космодрома Плесецк: ракетой-носителем «Восток-2» на орбиту Земли выведен искусственный спутник «Космос-112».


#Biography #Birth_Date #Events #17марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
[Совет по Java #016]

Тема: Ленивая загрузка в Hibernate (FetchType.LAZY) не работает после закрытия сессии.

Проблема: Hibernate использует шаблон ленивой загрузки для оптимизации производительности — связанные сущности подгружаются только при первом обращении к ним.

Однако для этого необходима открытая сессия (persistence context). Когда сессия закрывается (например, после завершения транзакции), доступ к неинициализированным прокси-объектам вызывает исключение LazyInitializationException.

Это классическая проблема при передаче сущностей на уровень представления (view) или при работе с ними вне транзакционного контекста.

Решение: Существует три основных подхода.

Первый — продлить транзакцию на весь сервисный метод, чтобы сессия оставалась открытой до полной обработки данных.
Второй — инициализировать все необходимые связи явно через JOIN FETCH в HQL/JPQL или через EntityGraph.
Третий — использовать паттерн Open Session in View (OSIV), который держит сессию открытой до завершения HTTP-запроса, но это антипаттерн для production из-за проблем с производительностью и риска утечек соединений.

@Entity
public class Order {
@Id private Long id;

@ManyToOne(fetch = FetchType.LAZY)
private User user; // ленивая загрузка

@OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
private List<Item> items;
// геттеры/сеттеры
}

@Service
public class OrderService {

@PersistenceContext
private EntityManager em;

//Антипаттерн: сессия закрыта при доступе к ленивым полям
public Order badFind(Long id) {
Order order = em.find(Order.class, id);
// Транзакция закончилась, сессия закрыта
order.getUser().getName(); // LazyInitializationException!
return order;
}

//Решение 1: @Transactional на уровне сервиса
@Transactional
public Order goodFindTransactional(Long id) {
Order order = em.find(Order.class, id);
order.getUser().getName(); // Ок, сессия открыта
return order;
}

//Решение 2: JOIN FETCH в запросе
@Transactional(readOnly = true)
public Order findWithJoinFetch(Long id) {
return em.createQuery(
"SELECT o FROM Order o JOIN FETCH o.user WHERE o.id = :id", Order.class)
.setParameter("id", id)
.getSingleResult(); // user уже загружен
}

//Решение 3: EntityGraph
@Transactional(readOnly = true)
public Order findWithEntityGraph(Long id) {
EntityGraph<?> graph = em.getEntityGraph("Order.withUserAndItems");
return em.find(Order.class, id,
Collections.singletonMap("javax.persistence.fetchgraph", graph));
}
}

// DTO-подход — лучшая практика
@Data
class OrderDTO {
private Long id;
private String userName; // только нужные поля, без прокси
}

@Service
class OrderDTOService {
@PersistenceContext
private EntityManager em;

public OrderDTO findDTO(Long id) {
return em.createQuery(
"SELECT NEW com.example.OrderDTO(o.id, u.name) " +
"FROM Order o JOIN o.user u WHERE o.id = :id", OrderDTO.class)
.setParameter("id", id)
.getSingleResult(); // Никаких прокси, только данные
}
}


Объяснение: Корень проблемы — отделение уровня персистентности от уровня представления.

Самое надежное решение — не передавать сущности наружу, а преобразовывать их в DTO (Data Transfer Objects) внутри транзакции.

Если все же нужны сущности, то либо продлевайте транзакцию (@Transactional на весь метод контроллера — плохо), либо инициализируйте все необходимое до закрытия сессии. OSIV (spring.jpa.open-in-view=true) включен по умолчанию в Spring Boot — это удобно для разработки, но в production может держать соединения с БД долго и маскировать проблемы с производительностью.


#Java #советы
👍6
Что выведет код?

import java.util.*;

public class Task170326 {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add(null);
list1.add("text");
list1.remove(1);

List<String> list2 = new ArrayList<>();
list2.add("text");
list2.remove(0);

System.out.println(list1.isEmpty());
System.out.println(list2.isEmpty());
System.out.println(list1.size() == 0);
System.out.println(list2.size() == 0);
}
}


#Tasks
👍2
Что такое Maven и Gradle? 🤓

Ответ:

Maven
и Gradle — это инструменты для автоматизации сборки и управления зависимостями.

Maven использует XML (файл pom.xml) для конфигурации и строго следует концепции жизненного цикла. Он декларативен — вы описываете, что хотите, а не как.

Gradle использует Groovy или Kotlin DSL, что делает скрипты более компактными и гибкими. Gradle основан на графе задач (task-based) и поддерживает инкрементальную сборку, что часто делает его быстрее Maven.

Gradle предлагает более плавный и мощный способ настройки сборки.


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
История технологий сегодня — 18 марта

ℹ️ Кто родился в этот день

Павел Игнатьевич Гроховский (18 марта 1899, Вязьма, Смоленская губерния — 29 мая 1943, Коммунарка)один из идеологов и родоначальников воздушно-десантных сил СССР, советский конструктор, изобретатель и организатор производства парашютной, авиационной и воздушно-десантной техники. Создал первые в мире хлопчатобумажные парашюты, парашютные системы и автоматические устройства к ним, грузовые контейнеры для воздушно-десантных войск.


🌐 Знаковые события

1965 — советский космонавт Алексей Леонов совершил первый в истории человечества выход в открытый космос.

1992 — компания Майкрософт представляет операционную систему Windows 3.1.


#Biography #Birth_Date #Events #18марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Раздел 8. Stream API и функциональный стиль в Java

Глава 6: Parallel Stream

Механика ForkJoinPool.commonPool()

Вызов .parallelStream() или .parallel() на потоке создаёт иллюзию простоты: система сама разделит работу между ядрами процессора, ускорит выполнение, вернёт результат. Реальность сложнее. Параллельные потоки в Java построены на ForkJoinPool — специализированном механизме для задач с шаблоном "разделяй и властвуй", и их эффективность зависит от понимания внутренней механики.


ForkJoinPool: архитектура общего пула

ForkJoinPool.commonPool() — статический пул, создаваемый при загрузке класса ForkJoinTask. Его размер по умолчанию равен количеству доступных процессоров минус один (оставляя один поток для основной работы), но не менее одного. Максимальный размер ограничен 32767 потоками, но на практике редко превышает десятки.

Этот пул — разделяемый ресурс. Он используется не только для parallelStream, но и для CompletableFuture.async, Arrays.parallelSort, RecursiveTask и других компонентов стандартной библиотеки. Исчерпание пула блокирующими задачами парализует всё приложение.


Разделение данных: Spliterator.trySplit()

Ключ к параллелизму — способность разделить источник данных на независимые части. Эту функцию выполняет Spliterator.trySplit():
public interface Spliterator<T> {
Spliterator<T> trySplit(); // Возвращает новый Spliterator для части данных или null
void forEachRemaining(Consumer<? super T> action);
boolean tryAdvance(Consumer<? super T> action);
long estimateSize();
int characteristics();
}


Метод trySplit() пытается разделить оставшиеся элементы пополам. Если успешно — возвращает новый Spliterator для первой половины, текущий продолжает обрабатывать вторую. Если данных мало или разделение невозможно — возвращает null, сигнализируя, что эту часть нужно обрабатывать последовательно.

Качество разделения определяет эффективность параллелизма:
Идеальные источники (ArrayList, массивы, IntStream.range): знают свой размер, поддерживают произвольный доступ. ArrayListSpliterator вычисляет середину как (lo + hi) >>> 1 и создаёт новый сплитератор для поддиапазона за O(1).
Приемлемые источники (HashSet, TreeSet): HashSet разделяется по бакетам хеш-таблицы. Разделение неравномерное (некоторые бакеты пусты), но работает. TreeSet использует структуру дерева для разделения.
Проблемные источники (LinkedList, Stream.iterate, Stream.generate, BufferedReader.lines()): не поддерживают произвольный доступ. LinkedList должен проходить узлы от начала до середины для разделения — O(n) на каждый split.

Stream.iterate и generate вообще не делятся, возвращая null из trySplit(). Параллельная обработка таких источников сводится к последовательной с накладными расходами на координацию.


Работа воркеров и кража задач

ForkJoinPool использует модель "work-stealing" (кража работы). Каждый поток-пул имеет локальную двустороннюю очередь (deque) задач. Новые задачи добавляются в голову очереди владельцем, выполняются с головы (LIFO — последняя добавленная первой). Когда поток опустошает свою очередь, он "ворует" задачи с хвоста очереди другого потока (FIFO — старые задачи), уменьшая contention.

Алгоритм для parallelStream:
Инициация: терминальная операция оборачивает конвейер в ForkJoinTask и отправляет в пул.
Разделение: корневой Spliterator делится рекурсивно, пока части достаточно малы или достигнут лимит параллелизма.
Выполнение: воркеры забирают подзадачи, обрабатывают свои сегменты данных через Spliterator.forEachRemaining.
Слияние: результаты подзадач комбинируются через Collector.combiner или аналогичный механизм.
Завершение: финальный результат возвращается вызывающему потоку.

Критично понимать: разделение происходит до выполнения, не во время. Поток разбивается на сегменты, затем каждый сегмент обрабатывается целиком одним воркером. Это не "потоковая" параллелизация, где элементы распределяются по ядрам по мере готовности, а "батчевая": данные разделены, затем обработаны.


#Java #для_новичков #beginner #stream_api #ForkJoinPool #parallelStream
👍4
Опасность блокирующих задач

Самый разрушительный антипаттерн — блокирующие операции внутри parallelStream.

Рассмотрим сценарий:

List<Result> results = urls.parallelStream()
.map(url -> {
try {
return httpClient.fetch(url); // Блокирующий HTTP-запрос, 500мс
} catch (IOException e) {
throw new UncheckedIOException(e);
}
})
.collect(toList());


При 100 URL и пуле размером 8 все воркеры быстро блокируются в ожидании сети. Оставшиеся 92 URL стоят в очереди. Но хуже: другие компоненты приложения, использующие commonPool (CompletableFuture, другие parallelStream), не получают потоков. Система "замораживается" — не от зависания, а от исчерпания ресурса.

Ещё хуже с Thread.sleep:
// Имитация тяжёлой работы
IntStream.range(0, 1000).parallel()
.map(i -> {
try { Thread.sleep(100); } catch (InterruptedException e) { }
return i * 2;
})
.collect(toList());


Все воркеры спят. Никакая другая задача в пуле не выполняется. Это эквивалентно deadlock для всего, что зависит от commonPool.

Диагностика в production:
Мониторинг ForkJoinPool.commonPool() через JMX: getActiveThreadCount(), getQueuedTaskCount(), getStealCount().
Thread dumps: поиск потоков с именем вида ForkJoinPool.commonPool-worker-N, ожидающих в Object.wait(), Thread.sleep(), или блокирующих I/O.
Профилирование: высокое время ожидания в ForkJoinTask.join() при отсутствии CPU-bound работы указывает на блокировки.


Альтернативы для блокирующих операций

Для I/O-bound задач parallelStream неприменим.

Используйте:
CompletableFuture с кастомным пулом:
ExecutorService ioPool = Executors.newFixedThreadPool(50);  // Много потоков, не боится блокировок

List<CompletableFuture<Result>> futures = urls.stream()
.map(url -> CompletableFuture.supplyAsync(() -> fetch(url), ioPool))
.collect(toList());

List<Result> results = futures.stream()
.map(CompletableFuture::join)
.collect(toList());

ioPool.shutdown();
Virtual Threads (Java 21+):
java
Copy
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
List<StructuredTaskScope.Subtask<Result>> subtasks = urls.stream()
.map(url -> scope.fork(() -> fetch(url)))
.collect(toList());

scope.join().throwIfFailed();

return subtasks.stream()
.map(StructuredTaskScope.Subtask::get)
.collect(toList());
}


Виртуальные потоки не блокируют носитель потоков ОС при блокировке Java-потока, делая блокирующие операции дешёвыми.


Контроль над commonPool

Размер пула можно настроить через системное свойство:

// При запуске JVM
-Djava.util.concurrent.ForkJoinPool.common.parallelism=16

// Или программно, но только до первого использования пула
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "16");


Но увеличение размера не решает проблему блокирующих задач — оно лишь откладывает исчерпание. Правильное решение — изоляция: блокирующие задачи в отдельном пуле, CPU-bound задачи в commonPool.


#Java #для_новичков #beginner #stream_api #ForkJoinPool #parallelStream
👍5
Что выведет код?

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

public class Task180326 {
public static void main(String[] args) {
ForkJoinPool pool = ForkJoinPool.commonPool();
int result = pool.invoke(new Task(10));
System.out.println(result);
}

static class Task extends RecursiveTask<Integer> {
int n;
Task(int n) { this.n = n; }

@Override
protected Integer compute() {
if (n <= 1) return n;

Task t1 = new Task(n - 1);
Task t2 = new Task(n - 2);

t1.fork();
return t2.compute() + t1.join();
}
}
}


#Tasks
👍3
Варианты ответа:
Anonymous Quiz
63%
55
13%
34
13%
89
13%
0
👍2
Что такое REST и какие принципы лежат в его основе? 🤓

Ответ:

REST (Representational State Transfer)
— это архитектурный стиль построения распределенных систем, чаще всего веб-сервисов.

Основные принципы:
Клиент-сервер (разделение ответственности).
Отсутствие состояния (Stateless) — сервер не хранит состояние клиента между запросами.
Кэширование ответов.
Единообразие интерфейса (Uniform Interface) — использование стандартных HTTP методов (GET, POST, PUT, DELETE) для работы с ресурсами, идентифицируемыми по URL.
Слои (Layered System).

RESTful сервисы обычно возвращают данные в формате JSON или XML.


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4