🔧 Micrometer: кастомные метрики которые реально полезны в production
Стандартные метрики Spring Boot (JVM, HTTP, datasource) — это база. Бизнес-метрики и метрики производительности конкретных операций пишутся вручную.
🔹 Решение
▪️ Базовые типы метрик
▪️ Тег-стратегия для разрезания метрик
В Grafana это даёт возможность делать rate(external_api_calls_total{status="failure"}[5m]) и смотреть ошибки по сервисам отдельно.
▪️ application.yml для Prometheus
publishPercentileHistogram() — ключевая настройка для Timer. Без неё Prometheus получает только count/sum и заранее вычисленные перцентили. С ней — полную гистограмму, из которой Prometheus сам считает перцентили через histogram_quantile().
Это важно при агрегации метрик с нескольких инстансов: заранее вычисленные перцентили нельзя корректно агрегировать, гистограммы — можно.
⚠️ Каждая уникальная комбинация тегов создаёт отдельный time series в Prometheus. Теги с высокой кардинальностью (userId, orderId, IP-адрес) убивают Prometheus. Теги должны иметь конечное и небольшое множество значений.
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#Enterprise
Стандартные метрики Spring Boot (JVM, HTTP, datasource) — это база. Бизнес-метрики и метрики производительности конкретных операций пишутся вручную.
🔹 Решение
▪️ Базовые типы метрик
@Service
public class OrderMetrics {
private final Counter ordersCreated;
private final Counter ordersFailed;
private final Timer orderProcessingTime;
private final DistributionSummary orderAmountSummary;
private final AtomicInteger activeOrders;
public OrderMetrics(MeterRegistry registry) {
this.ordersCreated = Counter.builder("orders.created")
.description("Total orders created")
.tag("environment", "production")
.register(registry);
this.ordersFailed = Counter.builder("orders.failed")
.description("Total failed orders")
.register(registry);
this.orderProcessingTime = Timer.builder("orders.processing.time")
.description("Order processing duration")
.publishPercentiles(0.5, 0.95, 0.99)
.publishPercentileHistogram()
.sla(Duration.ofMillis(200), Duration.ofSeconds(1))
.register(registry);
this.orderAmountSummary = DistributionSummary.builder("orders.amount")
.description("Order amounts distribution")
.baseUnit("USD")
.publishPercentiles(0.5, 0.95)
.register(registry);
this.activeOrders = registry.gauge("orders.active",
new AtomicInteger(0));
}
public Order processOrder(OrderRequest request) {
activeOrders.incrementAndGet();
return orderProcessingTime.record(() -> {
try {
Order order = doProcess(request);
ordersCreated.increment();
orderAmountSummary.record(order.getAmount().doubleValue());
return order;
} catch (Exception e) {
ordersFailed.increment();
throw e;
} finally {
activeOrders.decrementAndGet();
}
});
}
}
▪️ Тег-стратегия для разрезания метрик
Timer.builder("external.api.calls")
.tag("service", "payment-gateway")
.tag("operation", "charge")
.tag("status", result.isSuccess() ? "success" : "failure")
.register(registry)
.record(duration);В Grafana это даёт возможность делать rate(external_api_calls_total{status="failure"}[5m]) и смотреть ошибки по сервисам отдельно.
▪️ application.yml для Prometheus
management:
endpoints:
web:
exposure:
include: health,info,prometheus,metrics
metrics:
distribution:
percentiles-histogram:
http.server.requests: true
slo:
http.server.requests: 50ms,200ms,500ms,1s
tags:
application: ${spring.application.name}
environment: ${APP_ENV:local}
publishPercentileHistogram() — ключевая настройка для Timer. Без неё Prometheus получает только count/sum и заранее вычисленные перцентили. С ней — полную гистограмму, из которой Prometheus сам считает перцентили через histogram_quantile().
Это важно при агрегации метрик с нескольких инстансов: заранее вычисленные перцентили нельзя корректно агрегировать, гистограммы — можно.
══════ Навигация ══════
Вакансии • Задачи • Собесы
#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥1👏1
🧵 Просто о сложном: ThreadLocal
В многопоточном приложении потоки разделяют общую память. Это создаёт проблемы с состоянием. ThreadLocal решает это элегантно — каждый поток получает свою копию переменной.
🔹 Как устроено внутри
Никакой центральной Map на уровне JVM нет. Всё проще: у каждого объекта Thread есть поле threadLocals типа ThreadLocal.ThreadLocalMap. Это внутренняя Map, которая живёт внутри самого потока.
Когда ты вызываешь set() — значение кладётся в Map текущего потока, где ключ — сам объект ThreadLocal:
get() — достаёт из Map того же потока. Другой поток обращается к своей Map и видит своё значение. Пересечений нет по конструкции.
🔹 Инициализация через withInitial
Если get() вызван, а set() ещё не было, возвращается null. Чтобы этого избежать, используют фабрику:
Лямбда вызовется один раз на поток, при первом get(). Дальше кешируется в той же threadLocals Map.
🔹 Что реально использует ThreadLocal в экосистеме
Spring держит через него всё, что должно быть доступно в рамках одного запроса без явной передачи через параметры:
→ TransactionSynchronizationManager — текущее соединение с БД, чтобы сервис и репозиторий в рамках @Transactional работали с одним коннектом
→ SecurityContextHolder — аутентифицированный пользователь
→ RequestContextHolder — текущий HttpServletRequest
Идея одна: вместо того чтобы тащить контекст через каждый метод, кладёшь его в ThreadLocal на входе в поток и достаёшь где нужно.
🔹 Где ломается
Thread Pool. Потоки переиспользуются и после обработки запроса поток возвращается в пул, но его threadLocals Map никуда не девается. Если не вызвать remove(), то следующий запрос, попавший в этот поток, найдёт чужие данные.
Это не гипотетическая проблема. Именно так утекают security-контексты и появляются баги, которые воспроизводятся только под нагрузкой.
Второй момент — утечка памяти. ThreadLocalMap использует WeakReference на ключ, но значение держится сильной ссылкой. Если поток живёт долго (а в пуле — фактически вечно) и remove() никто не вызвал — объект не будет собран GC, пока поток жив.
Поэтому паттерн всегда один:
ThreadLocal решает конкретную задачу — изоляция состояния без синхронизации. Не серебряная пуля, не замена synchronized. Просто инструмент для случаев, когда данные должны жить в рамках потока, а не шариться между ними.
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#CoreJava
В многопоточном приложении потоки разделяют общую память. Это создаёт проблемы с состоянием. ThreadLocal решает это элегантно — каждый поток получает свою копию переменной.
🔹 Как устроено внутри
Никакой центральной Map на уровне JVM нет. Всё проще: у каждого объекта Thread есть поле threadLocals типа ThreadLocal.ThreadLocalMap. Это внутренняя Map, которая живёт внутри самого потока.
Когда ты вызываешь set() — значение кладётся в Map текущего потока, где ключ — сам объект ThreadLocal:
// упрощённо, внутри ThreadLocal.set(value)
Thread.currentThread().threadLocals.put(this, value);
get() — достаёт из Map того же потока. Другой поток обращается к своей Map и видит своё значение. Пересечений нет по конструкции.
🔹 Инициализация через withInitial
Если get() вызван, а set() ещё не было, возвращается null. Чтобы этого избежать, используют фабрику:
ThreadLocal<SimpleDateFormat> sdf =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
Лямбда вызовется один раз на поток, при первом get(). Дальше кешируется в той же threadLocals Map.
🔹 Что реально использует ThreadLocal в экосистеме
Spring держит через него всё, что должно быть доступно в рамках одного запроса без явной передачи через параметры:
→ TransactionSynchronizationManager — текущее соединение с БД, чтобы сервис и репозиторий в рамках @Transactional работали с одним коннектом
→ SecurityContextHolder — аутентифицированный пользователь
→ RequestContextHolder — текущий HttpServletRequest
Идея одна: вместо того чтобы тащить контекст через каждый метод, кладёшь его в ThreadLocal на входе в поток и достаёшь где нужно.
🔹 Где ломается
Thread Pool. Потоки переиспользуются и после обработки запроса поток возвращается в пул, но его threadLocals Map никуда не девается. Если не вызвать remove(), то следующий запрос, попавший в этот поток, найдёт чужие данные.
Это не гипотетическая проблема. Именно так утекают security-контексты и появляются баги, которые воспроизводятся только под нагрузкой.
Второй момент — утечка памяти. ThreadLocalMap использует WeakReference на ключ, но значение держится сильной ссылкой. Если поток живёт долго (а в пуле — фактически вечно) и remove() никто не вызвал — объект не будет собран GC, пока поток жив.
Поэтому паттерн всегда один:
try {
tl.set(value);
// работа
} finally {
tl.remove();
}ThreadLocal решает конкретную задачу — изоляция состояния без синхронизации. Не серебряная пуля, не замена synchronized. Просто инструмент для случаев, когда данные должны жить в рамках потока, а не шариться между ними.
══════ Навигация ══════
Вакансии • Задачи • Собесы
#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤5🔥2
У «Библиотеки программиста» появился резервный канал в мессенджере MAX
Он нужен исключительно для связи с теми, кто не может следить за обновлениями здесь из-за трудностей с доступом. Поэтому, если вы видите это сообщение, распространите его среди жильцов вашего ЖЭКа.
Контент в MAX будет дублировать телеграмный — основной нашей площадкой был и остаётся Telegram. Надеемся, это временная мера.
→ Подписаться на «Библиотеку программиста» в MAX
Он нужен исключительно для связи с теми, кто не может следить за обновлениями здесь из-за трудностей с доступом. Поэтому, если вы видите это сообщение, распространите его среди жильцов вашего ЖЭКа.
Контент в MAX будет дублировать телеграмный — основной нашей площадкой был и остаётся Telegram. Надеемся, это временная мера.
→ Подписаться на «Библиотеку программиста» в MAX
😢18🌚4😁2🥱1
Кажется, мы окончательно перешли от игрушек к суровому AgentOps
Приглашаем на наш обновлённый курс по разработке ИИ-агентов. Никакой воды про «будущее нейросетей», только инженерный подход.
На курсе мы:
— пошагово строим готовые системы на
— настраиваем кэширование и роутинг, чтобы бот не сожрал токены;
— разбираемся со стейтом, учимся дебажить через time-travel и прикручиваем human-in-the-loop;
— выводим RAG в прод так, чтобы безопасники не завернули архитектуру из-за 152-ФЗ.
В пекло скучные лекции про общую инфраструктуру — сразу фокусируемся на агентных фреймворках и написании кода. Занятия ведут бывалые лиды из Газпромбанка и Альфы, набившие шишки на реальных задачах.
Сегодня последний день, когда можно забрать курс по старым ценам. Базовый тариф сейчас стоит 49 000 ₽ (вместо 62 990 ₽), продвинутый трек — 99 000 ₽ (вместо 124 990 ₽). Если не хочется отдавать всю сумму сразу, есть рассрочка. Торопитесь — на потоке осталось всего 5 мест!
→ Зафиксировать цену и перейти к сборке своих агентов
Приглашаем на наш обновлённый курс по разработке ИИ-агентов. Никакой воды про «будущее нейросетей», только инженерный подход.
На курсе мы:
— пошагово строим готовые системы на
LangGraph, CrewAI и MCP;— настраиваем кэширование и роутинг, чтобы бот не сожрал токены;
— разбираемся со стейтом, учимся дебажить через time-travel и прикручиваем human-in-the-loop;
— выводим RAG в прод так, чтобы безопасники не завернули архитектуру из-за 152-ФЗ.
В пекло скучные лекции про общую инфраструктуру — сразу фокусируемся на агентных фреймворках и написании кода. Занятия ведут бывалые лиды из Газпромбанка и Альфы, набившие шишки на реальных задачах.
Кстати, на днях мы пилили агента в прямом эфире, если пропустили — есть запись вебинара.
Сегодня последний день, когда можно забрать курс по старым ценам. Базовый тариф сейчас стоит 49 000 ₽ (вместо 62 990 ₽), продвинутый трек — 99 000 ₽ (вместо 124 990 ₽). Если не хочется отдавать всю сумму сразу, есть рассрочка. Торопитесь — на потоке осталось всего 5 мест!
→ Зафиксировать цену и перейти к сборке своих агентов
😢1
Forwarded from Библиотека собеса по Java | вопросы с собеседований
Существует 4 основных уровня изоляции транзакций, каждый из которых определяет степень видимости данных, изменённых в одной транзакции, для других транзакций:
🔹 READ UNCOMMITTED
🔹 READ COMMITTED
🔹 REPEATABLE READ
🔹 SERIALIZABLE
Реализуется через блокировки (Lock-based): БД ставит range locks — блокирует не только существующие строки, но и диапазоны, куда могут вставиться новые. Это устраняет все виды аномалий, но может значительно снизить производительность.
#concurrency
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6👍4❤2
Please open Telegram to view this post
VIEW IN TELEGRAM
😁22💯4❤1👍1
🏭 Паттерн Factory Method
Factory Method — это порождающий паттерн, который предоставляет интерфейс для создания объектов в суперклассе, но позволяет подклассам изменять тип создаваемых объектов.
Использование
🔹 Когда тип создаваемого объекта зависит от контекста или конфигурации.
🔹 Когда нужно расширять систему новыми продуктами без правки существующего кода.
🔹 Когда создание объекта — это не просто new, а логика с условиями.
Преимущества
1️⃣ Ослабление связи между кодом и конкретными классами
При прямом вызове new вы жёстко привязаны к конкретному классу. Если его нужно заменить — придётся найти и исправить все точки создания. Фабрика убирает эту зависимость: код работает через интерфейс, конкретная реализация подставляется в одном месте.
2️⃣ Централизованное управление созданием объектов
Фабрика может содержать нетривиальную логику:
▪️ Выбор нужного класса в зависимости от условий.
▪️ Управление жизненным циклом объекта.
▪️ Применение Singleton или Object Pool внутри фабрики.
3️⃣ Расширяемость без правки существующего кода
Новый тип продукта — новый подкласс. Основной код не трогаете. Это и есть принцип Open/Closed в действии.
4️⃣ Упрощение тестирования
Фабрику можно подменить в тестах — подсунуть mock-реализацию. Это чище, чем патчить конструктор или использовать статические методы.
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#CoreJava
Factory Method — это порождающий паттерн, который предоставляет интерфейс для создания объектов в суперклассе, но позволяет подклассам изменять тип создаваемых объектов.
Использование
🔹 Когда тип создаваемого объекта зависит от контекста или конфигурации.
🔹 Когда нужно расширять систему новыми продуктами без правки существующего кода.
🔹 Когда создание объекта — это не просто new, а логика с условиями.
Преимущества
При прямом вызове new вы жёстко привязаны к конкретному классу. Если его нужно заменить — придётся найти и исправить все точки создания. Фабрика убирает эту зависимость: код работает через интерфейс, конкретная реализация подставляется в одном месте.
Фабрика может содержать нетривиальную логику:
▪️ Выбор нужного класса в зависимости от условий.
▪️ Управление жизненным циклом объекта.
▪️ Применение Singleton или Object Pool внутри фабрики.
Новый тип продукта — новый подкласс. Основной код не трогаете. Это и есть принцип Open/Closed в действии.
Фабрику можно подменить в тестах — подсунуть mock-реализацию. Это чище, чем патчить конструктор или использовать статические методы.
══════ Навигация ══════
Вакансии • Задачи • Собесы
#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍5👾2🔥1
Java Developer — ЮMoney — удалёнка
Java-разработчик — DUC Technologies — удалёнка
Senior Java Developer — Лектон — удалёнка/гибрид (Санкт-Петербург)
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍1
Многие из нас знают паттерны «по названиям». Но можешь ли объяснить, чем AbstractFactory отличается от FactoryMethod? Или когда Prototype — не оверинжиниринг, а реальное решение?
На Хабре вышла первая часть добротного разбора порождающих паттернов с примерами. Без воды, с живыми примерами на Kafka vs RabbitMQ, Spring @Component, Lombok @Builder.
👉 Читать на Хабре
══════ Навигация ══════
Вакансии • Задачи • Собесы
#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍5🔥1
Please open Telegram to view this post
VIEW IN TELEGRAM
😁23💯9👍1
🔐 Security-модуль за 5 минут с AI
Каждый раз одно и то же: новый проект, пишешь JWT с нуля, лезешь в старые репозитории за шаблоном, получаешь deprecated код на Spring Security 5.
Можно иначе — сформулировал промпт так, чтобы AI сразу выдал то, что не придётся переписывать.
Дальше подгоняешь под свою модель пользователя и роли. Основа готова.
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#Enterprise
Каждый раз одно и то же: новый проект, пишешь JWT с нуля, лезешь в старые репозитории за шаблоном, получаешь deprecated код на Spring Security 5.
Можно иначе — сформулировал промпт так, чтобы AI сразу выдал то, что не придётся переписывать.
Ты Senior Java-разработчик. Сгенерируй production-ready модуль безопасности для Spring Boot 3.x.
Требования:
- JWT (access + refresh токены), фильтр OncePerRequestFilter
- SecurityFilterChain: csrf off, session stateless
- UserDetailsService через JPA-репозиторий
- Пакеты: security/config, security/filter, security/service
- Обработка AuthenticationException и AccessDeniedException — отдельные handler-классы
- Никакого WebSecurityConfigurerAdapter
Стек: Java 21, Spring Boot 3.2, Spring Security 6, JJWT 0.12.x
Верни полный код всех классов с import и package.
Дальше подгоняешь под свою модель пользователя и роли. Основа готова.
══════ Навигация ══════
Вакансии • Задачи • Собесы
#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🔥2🌚1
Когда пользователь из Сиднея открывает ваш сайт, его запрос летит на сервер в Германии и обратно. Это ~300 мс только на «дорогу» — и это для каждого изображения, шрифта, JS-файла.
CDN (Content Delivery Network / Сеть доставки контента) решает эту проблему в лоб: вместо одного сервера — тысячи точек по всему миру. Пользователь получает файлы с ближайшей к нему ноды, а не с вашего origin-сервера.
🔹 Origin и Edge
В архитектуре с CDN появляются два новых понятия:
— Origin Server (Главный сервер): это ваш настоящий сервер, где крутится Spring Boot, лежит база данных и хранятся оригиналы всех файлов (например, в Amazon S3).
— Edge Servers (Граничные серверы / Точки присутствия - PoP): это тысячи серверов CDN-провайдера (например, Cloudflare или Akamai), раскиданные по всему земному шару: в Сиднее, Токио, Нью-Йорке, Лондоне, Москве.
🔹 Как работает кэш
Первый запрос к конкретному файлу всё равно идёт до origin. CDN его скачивает, кэширует у себя и отдаёт пользователю. Все последующие запросы на тот же файл edge-сервер обрабатывает сам, не трогая ваш бэкенд. Это называется cache hit.
🔹 Что кладем в CDN
CDN идеально подходит для статического контента:
— Картинки, видео, аудио.
— Скомпилированные файлы JavaScript и CSS.
— Шрифты.
CDN не подходит для динамического контента:
— Корзина товаров.
— Приватные данные пользователя.
— (Запросы к API
/api/v1/users/me должны идти напрямую на ваш сервер, минуя кэш CDN).🔹 Защита от DDoS
Современные CDN (тот же Cloudflare) - это не просто кэш. Это гигантский щит.
Если хакеры решат «положить» ваш сайт и отправят миллион запросов в секунду, этот удар примут на себя серверы CDN. Их пропускная способность измеряется терабитами. Они отфильтруют «мусорный» трафик, и ваш маленький Origin-сервер даже не заметит атаки.
🔹 Побочные эффекты
— Ускорение: пользователи получают тяжелый контент мгновенно, потому что скачивают его из своего же города.
— Экономия: ваш главный сервер больше не тратит процессорное время и трафик на отдачу терабайтов картинок. Вы платите за сервера меньше.
— Безопасность: CDN скрывает реальный IP-адрес вашего сервера и защищает от DDoS-атак.
══════ Навигация ══════
Вакансии • Задачи • Собесы
#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤4🔥1
Почитали тут свежий отчёт по рынку ИИ-ускорителей в РФ: оказывается, 54% компаний тормозят внедрение ИИ исключительно из-за конских цен на инфраструктуру.
Ну, то есть написать пет-проект с вызовом API это задача на вечер, а вот запустить агента в продакшн так, чтобы он не сжёг бюджет отдела за неделю — суровая инженерия.
По сути, сейчас мало уметь собирать RAG. Нужно считать токены, настраивать
Также в программе:
— оценка качества, трейсинг и защита от деградации пайплайнов;
— мультиагентные паттерны и интеграция по протоколу
— локальный деплой Open Source под 152-ФЗ (когда данные нельзя выносить наружу).
Кажется, это единственный адекватный roadmap по переходу от блокнотов к enterprise-решениям.
Прямо сейчас можно урвать курс с увесистой скидкой(49 000 ₽ 62 990 ₽ за базовый тариф и 99 000 ₽ 124 990 ₽ за продвинутый трек) , но стоит поторопиться — на потоке осталось всего 5 мест.
👉 Зафиксировать цену и начать собирать агентов, за которых не стыдно в проде
Ну, то есть написать пет-проект с вызовом API это задача на вечер, а вот запустить агента в продакшн так, чтобы он не сжёг бюджет отдела за неделю — суровая инженерия.
По сути, сейчас мало уметь собирать RAG. Нужно считать токены, настраивать
time-travel дебаг в LangGraph и уметь роутить запросы на лету. Всё это мы учли в обновлённом курсе по разработке AI-агентов, где акцент сделан именно на AgentOps и жёсткий контроль ресурсов.Также в программе:
— оценка качества, трейсинг и защита от деградации пайплайнов;
— мультиагентные паттерны и интеграция по протоколу
MCP;— локальный деплой Open Source под 152-ФЗ (когда данные нельзя выносить наружу).
Кажется, это единственный адекватный roadmap по переходу от блокнотов к enterprise-решениям.
Прямо сейчас можно урвать курс с увесистой скидкой
👉 Зафиксировать цену и начать собирать агентов, за которых не стыдно в проде
Forwarded from Библиотека собеса по Java | вопросы с собеседований
Метрики норм, тесты зелёные, при пике трафика — БД ложится 👇
📦 Задание — code review
Сервис отдаёт профили пользователей. Для ускорения добавили кэш на 5 минут.
@Service
@RequiredArgsConstructor
public class UserProfileService {
private final UserRepository userRepository;
private final RedisTemplate<String, UserProfile> redisTemplate;
private static final Duration TTL = Duration.ofMinutes(5);
public UserProfile getProfile(Long userId) {
String key = "profile:" + userId;
UserProfile cached = redisTemplate.opsForValue().get(key);
if (cached != null) {
return cached;
}
UserProfile profile = userRepository.findById(userId)
.map(UserProfile::from)
.orElseThrow(UserNotFoundException::new);
redisTemplate.opsForValue().set(key, profile, TTL);
return profile;
}
}
▪️ Объясни
— Что именно происходит при истечении TTL под нагрузкой.
— Почему добавление @Cacheable над методом не спасёт ситуацию.
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥20👍4❤2
Please open Telegram to view this post
VIEW IN TELEGRAM
😁22💯7❤🔥1🔥1
Представь, что есть цикл на 10 000 элементов, и баг воспроизводится только на одном конкретном объекте. Без Conditional Breakpoints придется жать F8 вручную сотни раз. А с ними дебаггер сам остановится в нужный момент.
🔹 Как включить
Кликни правой кнопкой на кружок брейкпоинта → появится поле Condition. Введите любое булево Java-выражение. Всё, дебаггер будет останавливаться только когда оно true.
🔹 Примеры из реальной жизни
▪️ Фильтрация по ID
user.getId() == 42
Останавливаемся только на конкретном пользователе — удобно при обработке списка сущностей из БД.
▪️ Фильтрация по содержимому строки
request.getUrl().contains("/admin")Отлавливаем только определённые HTTP-запросы в фильтре или интерцепторе.
▪️ Ловим NPE до того, как он случился
order.getItems() == null
Останавливаемся именно тогда, когда данные уже сломаны, а не после падения.
▪️ Условие по индексу в цикле
i == 999
Прыгаем сразу к последней итерации, не прокручивая весь цикл.
🔹 Продвинутые трюки
— Log message вместо остановки. Если не хотите прерывать выполнение, а просто логировать — в том же окне брейкпоинта включите "Evaluate and log" и введите выражение.
— Pass count. Чуть ниже в настройках брейкпоинта есть поле "Pass count" — брейкпоинт сработает только на N-м попадании.
— Disable until hit. Можно настроить цепочку: один брейкпоинт активирует другой. В настройках есть "Disable until breakpoint is hit" — указываете другой брейкпоинт, и текущий начнёт работать только после срабатывания того.
══════ Навигация ══════
Вакансии • Задачи • Собесы
#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤3🔥1👏1
Forwarded from Библиотека задач по Java | тесты, код, задания
Какие принципы нарушены в коде?
Anonymous Quiz
9%
DRY и S из SOLID
12%
O и I из SOLID
50%
D и S из SOLID
9%
KISS и S из SOLID
21%
Посмотреть ответ
👍7
Please open Telegram to view this post
VIEW IN TELEGRAM
😁24😢5🌚3
🧵 StampedLock: когда ReentrantReadWriteLock уже не справляется
Если знаком с ReentrantReadWriteLock, то знаешь идею: много читателей одновременно, но запись эксклюзивна. Звучит разумно, но у этой модели есть фундаментальная проблема: читатели блокируют писателей.
Поток, который хочет писать, ждёт пока все читатели закончат. При высокой read-нагрузке писатель может ждать очень долго. StampedLock решает именно это.
🔵 Три режима, не два
ReentrantReadWriteLock даёт два режима — read и write. StampedLock добавляет третий — optimistic read.
Это знакомо. Но вот где начинается интересное:
tryOptimisticRead() не захватывает никакого лока, а просто возвращает штамп. validate() проверяет — была ли запись с момента получения штампа. Если нет, то данные консистентны, идём дальше, если да, то фолбэк на обычный read lock.
🔵 Что такое stamp
Это long, который кодирует состояние лока. Каждая успешная запись инвалидирует все оптимистичные штампы. Поэтому validate() — это просто битовая проверка, без CAS, без синхронизации, так что она очень дешёвая.
🔵 Где это реально даёт профит
Сценарий: данные читаются в 95% случаев, пишутся редко. Классический пример — кеш, конфигурация, счётчики с инкрементом.
С ReentrantReadWriteLock все читатели всё равно захватывают shared lock — это операция на AQS, есть накладные расходы. С StampedLock + optimistic read при отсутствии конкурентных записей блокировки нет вообще.
🔵 Подводные камни
— StampedLock не реентерабельный. Если поток захватил write lock и попытается захватить его снова — дедлок. Это принципиальное отличие от ReentrantReadWriteLock.
— Нет поддержки Condition. Если тебе нужен await/signal — StampedLock не подойдёт.
— Нет поддержки прерывания в обычных методах readLock()/writeLock(). Есть отдельные readLockInterruptibly() — но это уже осознанный выбор.
StampedLock — инструмент для конкретного профиля нагрузки: много чтений, редкие записи, нет реентерабельности. Не замена всем локам подряд. Но в правильном месте даёт ощутимый прирост без усложнения архитектуры.
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#CoreJava
Если знаком с ReentrantReadWriteLock, то знаешь идею: много читателей одновременно, но запись эксклюзивна. Звучит разумно, но у этой модели есть фундаментальная проблема: читатели блокируют писателей.
Поток, который хочет писать, ждёт пока все читатели закончат. При высокой read-нагрузке писатель может ждать очень долго. StampedLock решает именно это.
ReentrantReadWriteLock даёт два режима — read и write. StampedLock добавляет третий — optimistic read.
StampedLock lock = new StampedLock();
// обычный read lock
long stamp = lock.readLock();
try {
// читаем
} finally {
lock.unlockRead(stamp);
}
// write lock
long stamp = lock.writeLock();
try {
// пишем
} finally {
lock.unlockWrite(stamp);
}
Это знакомо. Но вот где начинается интересное:
long stamp = lock.tryOptimisticRead();
// читаем данные без блокировки вообще
int value = this.value;
if (!lock.validate(stamp)) {
// кто-то писал пока мы читали — перечитываем под реальным локом
stamp = lock.readLock();
try {
value = this.value;
} finally {
lock.unlockRead(stamp);
}
}
tryOptimisticRead() не захватывает никакого лока, а просто возвращает штамп. validate() проверяет — была ли запись с момента получения штампа. Если нет, то данные консистентны, идём дальше, если да, то фолбэк на обычный read lock.
Это long, который кодирует состояние лока. Каждая успешная запись инвалидирует все оптимистичные штампы. Поэтому validate() — это просто битовая проверка, без CAS, без синхронизации, так что она очень дешёвая.
Сценарий: данные читаются в 95% случаев, пишутся редко. Классический пример — кеш, конфигурация, счётчики с инкрементом.
С ReentrantReadWriteLock все читатели всё равно захватывают shared lock — это операция на AQS, есть накладные расходы. С StampedLock + optimistic read при отсутствии конкурентных записей блокировки нет вообще.
— StampedLock не реентерабельный. Если поток захватил write lock и попытается захватить его снова — дедлок. Это принципиальное отличие от ReentrantReadWriteLock.
— Нет поддержки Condition. Если тебе нужен await/signal — StampedLock не подойдёт.
— Нет поддержки прерывания в обычных методах readLock()/writeLock(). Есть отдельные readLockInterruptibly() — но это уже осознанный выбор.
StampedLock — инструмент для конкретного профиля нагрузки: много чтений, редкие записи, нет реентерабельности. Не замена всем локам подряд. Но в правильном месте даёт ощутимый прирост без усложнения архитектуры.
══════ Навигация ══════
Вакансии • Задачи • Собесы
#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥5👏1
😁 Развернем холиварчик
К статье на Хабре «Я два месяца платил 300к человеку, который тихо скармливал мои задачи в ChatGPT» уже почти 900 комментов.
Прочитайте, давайте тоже обсудим💬
🐸 Библиотека джависта
#DevLife
К статье на Хабре «Я два месяца платил 300к человеку, который тихо скармливал мои задачи в ChatGPT» уже почти 900 комментов.
Прочитайте, давайте тоже обсудим
🐸 Библиотека джависта
#DevLife
Please open Telegram to view this post
VIEW IN TELEGRAM
😁8👍1🔥1