Библиотека джависта | Java, Spring, Maven, Hibernate
23.5K subscribers
2.16K photos
44 videos
45 files
3.04K links
Все самое полезное для Java-разработчика в одном канале.

Список наших каналов: https://t.me/proglibrary/9197

Для обратной связи: @proglibrary_feeedback_bot

По рекламе: @proglib_adv

РКН: https://gosuslugi.ru/snet/67a5bbda1b17b35b6c1a55c4
Download Telegram
🔍 Просто о сложном: CompletableFuture

В Java 8 появился CompletableFuture — это реализация паттерна Promise, которая позволяет строить декларативные цепочки асинхронных операций.

По сути, это обёртка над Future, которая может быть завершена вручную (отсюда "Completable") и предоставляет богатый API для композиции.

🔹 Зачем он нужен

Классический Future не позволяет:

— Комбинировать несколько асинхронных операций.
— Обрабатывать результат без блокировки.
— Реагировать на ошибки внутри цепочки.

CompletableFuture решает эти проблемы, предоставляя fluent API для композиции асинхронных вычислений.

🔹 Базовый пример
javaCompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> fetchUserFromDB(userId))
.thenApply(user -> user.getEmail())
.thenApply(String::toUpperCase)
.exceptionally(ex -> "default@example.com");

future.thenAccept(System.out::println); // не блокирует


Здесь каждый этап выполняется асинхронно. Если где-то произошла ошибка, сработает exceptionally().

🔹 Ключевые методы

▪️ supplyAsync() / runAsync() — запустить задачу асинхронно.
▪️ thenApply() — трансформировать результат.
▪️ thenAccept() — обработать результат (void).
▪️ thenCompose() — развернуть вложенный CompletableFuture.
▪️ thenCombine() — объединить результаты двух независимых future.
▪️ exceptionally() / handle() — обработка ошибок.
▪️ allOf() / anyOf() — дождаться завершения нескольких задач.

🔹 Пулы потоков

По умолчанию CompletableFuture использует ForkJoinPool.commonPool(). Для задач с блокирующими операциями (IO, БД) лучше передать свой Executor. Иначе можно заблокировать общий пул и замедлить всё приложение.

🔹 Подводные камни

— Отсутствие отмены

CompletableFuture.cancel() не останавливает выполнение задачи, а только меняет статус. Реальная отмена требует проверки Thread.interrupted() внутри задачи.

— Проглатывание исключений


Если не добавить exceptionally() или handle(), исключение останется внутри future до вызова get() или join().

— Цепочки могут выполняться синхронно

Методы без суффикса Async (например, thenApply) могут выполниться в том же потоке, где завершился предыдущий этап. Если нужна гарантия асинхронности, используйте thenApplyAsync().

✔️ Когда использовать

— Для композиции нескольких асинхронных операций (API-вызовы, запросы в БД).
— Когда нужны неблокирующие обработчики результатов.
— В реактивных архитектурах (хотя там лучше Project Reactor или RxJava).

Не подходит:

— Для CPU-bound задач с высокой конкуренцией (лучше использовать параллельные стримы или явное управление потоками).
— Когда важна отмена выполняющейся задачи.

🐸 Библиотека джависта

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍9🔥31
⚙️ Настройка email-уведомлений

Когда проектируешь систему с email-уведомлениями, важно понимать не только Spring Boot Starter Mail, но и что происходит уровнем ниже.

SMTP — это то, на чём всё держится. И если знать его команды и коды ответов, можно быстрее находить проблемы: почему письмо ушло в спам, где потерялось, какой сервер отклонил.

Про сам протокол можно почитать подробнее тут.

А так это выглядит на пратике👇🏻

🔵 Базовая настройка через JavaMail API

Properties props = new Properties();
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.port", "587");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");

Session session = Session.getInstance(props, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});

Message message = new MimeMessage(session);
message.setFrom(new InternetAddress("from@example.com"));
message.setRecipients(Message.RecipientType.TO,
InternetAddress.parse("to@example.com"));
message.setSubject("Тема письма");
message.setText("Текст сообщения");

Transport.send(message); // Здесь и запускается SMTP-танец


🔵 Через Spring Boot (проще в настройке):

@Service
public class EmailService {

@Autowired
private JavaMailSender mailSender;

public void sendEmail(String to, String subject, String text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(subject);
message.setText(text);

mailSender.send(message);
}
}

Зависимость:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>

ИЛИ

implementation 'org.springframework.boot:spring-boot-starter-mail'

application.yml:
spring:
mail:
host: smtp.gmail.com
port: 587
username: your-email@gmail.com
password: your-app-password
properties:
mail:
smtp:
auth: true
starttls:
enable: true


🔵 Полезные моменты:

🔹 starttls.enable — шифрование соединения (команда STARTTLS в SMTP)
🔹 mail.smtp.auth — аутентификация на сервере
🔹 Порты: 25 (обычный), 587 (TLS), 465 (SSL)

Когда понимаешь, что происходит на уровне протокола, легче дебажить: смотришь логи Transport, видишь SMTP-коды ответов (250 OK, 550 rejected и т.д.) и сразу понятно, где проблема.

🐸 Библиотека джависта

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍92🔥1
👀 Внутреннее устройство ConcurrentSkipListMap

ConcurrentSkipListMap — это потокобезопасная реализация NavigableMap из пакета java.util.concurrent.

В отличие от ConcurrentHashMap, эта структура поддерживает упорядоченность элементов и основана на вероятностной структуре данных Skip List (список с пропусками).

📦 Базовая структура

Внутри ConcurrentSkipListMap использует многоуровневый связанный список Skip List (см. фото).

Главная особенность:

— Элементы отсортированы по ключу (естественный порядок или Comparator).

— Несколько уровней индексации для быстрого поиска.

— Lock-free операции чтения через CAS (Compare-And-Swap).

— Минимальные блокировки только при изменении структуры.

🔍 Как устроено хранение

Базовые компоненты:

▪️ Node<K,V> — узел нижнего уровня.
▪️ Index<K,V> — узел индексного уровня.
▪️ HeadIndex<K,V> — голова уровня, хранит высоту.

Принцип работы

— Данные хранятся только на нижнем уровне (в Node).

— Верхние уровни содержат Index — указатели для быстрого прыжка.

— При вставке элемента случайно определяется высота (вероятность ~50% для каждого уровня).

— Максимальная высота ограничена 64 уровнями.

⚡️ Операции поиска, вставки и удаления

🔎 get(K key) — поиск

1. Начинается с верхнего уровня HeadIndex.
2. Движется вправо, пока ключ не станет больше искомого.
3. Спускается на уровень ниже.
4. Повторяет до нижнего уровня → O(log n).
5. Без блокировок, читает volatile-ссылки.

put(K key, V value) — вставка

1. Выполняется поиск позиции (как в get).
2. Если ключ существует, обновляется через CAS → O(log n).
3. Если новый:
— Создаётся новый Node.
— Вставляется в нижний уровень с CAS.
— Случайно генерируется высота (геометрическое распределение).
— Создаются Index-узлы для верхних уровней.
— Связываются с соседями через CAS.
4. При необходимости увеличивается высота всей структуры.

Блокировки: минимальны, только при изменении указателей через CAS-retry loops.

remove(Object key) — удаление

1. Находится узел на нижнем уровне.
2. Помечается как удалённый (marker node) через CAS.
3. Физически отсоединяется от списка.
4. Индексы на верхних уровнях удаляются постепенно (ленивое удаление).
5. Если верхние уровни опустели, высота уменьшается.

⚖️ Важные нюансы

1. Вероятностная балансировка

В отличие от AVL/Red-Black деревьев, балансировка достигается случайной высотой узлов.
Нет гарантии идеального баланса, но ожидаемая сложность O(log n).

2. Null и сравнения

Null ключи запрещены (NullPointerException).
Null значения допустимы (в отличие от ConcurrentHashMap).
Ключи должны быть Comparable или нужен Comparator.

3. Memory Consistency

Гарантии happens-before для put/get одного и того же ключа.
Weakly consistent bulk операции (putAll, clear).

✔️ Полезно для

— Упорядоченных concurrent-карт: логи по времени, рейтинги, приоритетные кэши.

— Range-запросы: subMap(from, to), headMap(to), tailMap(from).

— Навигация: firstEntry(), lastEntry(), floorEntry(key), ceilingEntry(key).

— Concurrent sorted set: keySet() даёт ConcurrentNavigableSet.

— Когда нужен порядок + потокобезопасность без глобальных блокировок.

🔗 Документация: JavaDoc (Java 17)

Ставьте 🔥, если хотите ещё разбор.

🐸 Библиотека джависта

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥92👍2