Java Geek
2.37K subscribers
291 photos
1 file
54 links
Практичные советы, лайфхаки и код для Java-разработчиков. Каждый пост — реальная польза. Учим Java на примерах.

По всем вопросам @evgenycarter
Download Telegram
🏗 Java: Структурная конкурентность. Прощайте, зомби-потоки!

Допустим, вы используете Виртуальные потоки (Project Loom), чтобы сделать два независимых запроса: получить данные пользователя (API 1) и его заказы (API 2).

Раньше мы использовали ExecutorService или CompletableFuture. Но у них есть огромная архитектурная дыра: они ничего не знают друг о друге.

🧟‍♂️ Проблема зомби-потоков (Unstructured Concurrency)

Если API 1 мгновенно падает с ошибкой 500, ваш метод все равно будет ждать, пока API 2 доработает (или упадет по таймауту). Поток, качающий заказы, становится "сиротой". Он делает бесполезную работу, тратит сеть и память, хотя результат уже никому не нужен.

А если ошибку выкинет родительский метод? Дочерние потоки продолжат жить своей жизнью в фоне. Это хаос.

🧩 Решение: StructuredTaskScope

В современной Java (начиная с 21 версии) потоки привязали к лексической области видимости - блоку кода. Если мы выходим из блока (из-за ошибки или успешного завершения), все запущенные внутри него дочерние потоки автоматически отменяются (получают interrupt).

Вот как выглядит "Запрос-Ответ", где должны выполниться оба действия (Стратегия *All or Nothing*):


// ShutdownOnFailure: если один упал, отменяем остальные
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {

// Запускаем подзадачи (Subtasks)
Subtask<User> user = scope.fork(() -> fetchUser(id));
Subtask<List<Order>> orders = scope.fork(() -> fetchOrders(id));

scope.join(); // Ждем завершения обеих задач...
scope.throwIfFailed(); // ...или первой же ошибки!

// Сюда дойдем, только если обе задачи успешны
return new UserProfile(user.get(), orders.get());
}
// При выходе из блока любые зависшие потоки будут убиты



🏎 Стратегия "Кто первый, тот и прав"

А что, если вам нужно получить курс валют, и у вас есть 3 разных провайдера? Вам нужен ответ от любого, кто ответит быстрее.


// ShutdownOnSuccess: первый успешный отменяет остальные
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<Quote>()) {

scope.fork(() -> getFromBankA());
scope.fork(() -> getFromBankB());
scope.fork(() -> getFromBankC());

scope.join(); // Ждем первого успешного

return scope.result(); // Возвращаем самый быстрый ответ
}



Как только Банк А ответит, запросы к Банкам B и C будут немедленно отменены. Никакого ручного управления Future.cancel(). Всё работает из коробки.

🧠 Почему это меняет всё?

1. Читаемость: Многопоточный код читается сверху вниз, как обычный синхронный.
2. Безопасность ресурсов: Утечки потоков физически невозможны. Структура гарантирует, что родитель не завершится, пока не разберется со всеми детьми.
3. Идеальные логи: Стек-трейс теперь показывает реальную иерархию (кто кого вызвал), а не обрывается на внутренностях пула потоков.

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

#Concurrency #ProjectLoom #CleanCode #Backend

📲 Мы в MAX

👉 @java_geek
👍1🔥1
🚀 Подборка полезных IT каналов в Max


Системное администрирование, DevOps 📌

https://max.ru/i_odmin Все для системного администратора
https://max.ru/bash_srv Bash Советы
https://max.ru/sysadminof Книги для админов, полезные материалы
https://max.ru/i_odmin_book Библиотека Системного Администратора
https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др.
https://max.ru/tipsysdmin Типичный Сисадмин
https://max.ru/channel_win_sysadmin Системный Администратор Windows
https://max.ru/channel_linux_admin Linux: Системный администратор
https://max.ru/channel_linuxmod Linux
https://max.ru/channel_i_linux Системный администратор
https://max.ru/channel_devopslib DevOps, SRE, Sysadmin
https://max.ru/channel_devops_star DevOps Star (Звезда Девопса)

Excel лайфхак 📌
https://t.me/Excel_lifehack Excel лайфхак

Английский с нуля 🇬🇧
https://max.ru/UchuEnglish

1C разработка 📌
https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С
https://max.ru/channel_DevLab1C 1С:Предприятие 8

Программирование C++📌
https://max.ru/cpp_lib Библиотека C/C++ разработчика
https://max.ru/channel_cpp_geek C++ geek

Программирование Go📌
https://max.ru/golang_lib Библиотека Go (Golang) разработчика

Программирование React📌
https://max.ru/react_lib React

Программирование Rust📌
https://max.ru/channel_rust_lib

Программирование Python 📌
https://max.ru/python_of Python академия.
https://max.ru/BookPython Библиотека Python разработчика

Java разработка 📌
https://max.ru/bookjava Библиотека Java разработчика
https://max.ru/channel_java_geek Java Geek

GitHub Сообщество 📌
https://max.ru/githublib Интересное из GitHub

Базы данных (Data Base) 📌
https://max.ru/database_info Все про базы данных

Фронтенд разработка 📌
https://max.ru/frontend_1 Подборки для frontend разработчиков

Библиотеки 📌
https://max.ru/programmist_of Книги по программированию
https://max.ru/proglb Библиотека программиста
https://max.ru/bfbook Книги для программистов

Программирование 📌
https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций
https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT
https://max.ru/php_lib Библиотека PHP программиста 👨🏼‍💻👩‍💻

Шутки программистов 📌
https://max.ru/itumor Шутки программистов

Защита, взлом, безопасность 📌
https://max.ru/thehaking Канал о кибербезопасности
https://max.ru/xakkep_1 Хакер Free

Книги, статьи для дизайнеров 📌
https://max.ru/odesigners Статьи, книги для дизайнеров

Математика 📌
https://max.ru/Pomatematike Канал по математике
https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике

Вакансии в IT📌
https://max.ru/progjob
https://max.ru/channel_rabotait

Мир технологий 📌
https://max.ru/mir_teh Канал для любознательных

Городские📌
https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга
https://max.ru/mockva_life Свежие новости Москвы
https://max.ru/piterspb Питер Новости: Санкт-Петербург / СПБ / ДТП
https://max.ru/channel_krasnodar_novosty Краснодар Новости
https://max.ru/channel_novosibirsk_novosti Новосибирск
https://max.ru/channel_samara_novosti Новости Самары
https://max.ru/channel_ekaterinburg_novosti Новости Екатеринбурга
https://max.ru/channel_kazan_novosti Новости Казани
https://max.ru/channel_omsk_novosti Новости Омска
https://max.ru/channel_moskva_24 Москва 24
🏗 System Design: Эволюция архитектуры от 1 до 1 000 000 пользователей

Главная ошибка разработчиков при проектировании систем - строить звездолет для поездки за хлебом. Микросервисы, Kafka и Kubernetes не нужны вашему стартапу в первый день.

Архитектура должна эволюционировать шаг за шагом. Вот как выглядит этот путь.

Уровень 1: Одинокий Волк (1 - 1000 юзеров)

Всё крутится на одном сервере (например, в DigitalOcean или AWS EC2).

• Что там: Ваше Java-приложение (Monolith) + база данных (PostgreSQL) + веб-сервер (Nginx) живут на одной машине.
• Плюсы: Развертывание занимает 5 минут, всё работает быстро (сетевые задержки нулевые).
• Минусы: Если сервер упал - упало всё. Масштабировать можно только покупкой более мощного процессора/памяти (Вертикальное масштабирование).

Уровень 2: Разделение труда (10 000 юзеров)

Приложение начинает тормозить, потому что СУБД "съела" всю оперативную память.

• Что делаем: Выносим базу данных на отдельный сервер. Желательно использовать управляемое решение (Managed DB от облачного провайдера), чтобы не возиться с бэкапами.
• Результат: Приложение и БД больше не дерутся за ресурсы.

Уровень 3: Горизонтальное масштабирование (100 000 юзеров)

Трафик растет. Один сервер приложения больше не справляется с HTTP-запросами.

• Что делаем: Ставим Load Balancer (Балансировщик нагрузки) и поднимаем 3-5 одинаковых серверов с вашим Java-приложением.
• Правило: Ваше приложение должно стать Stateless (без состояния). Вы больше не можете хранить сессии пользователей в локальной памяти (RAM), иначе юзер залогинится на Сервере 1, а следующий запрос попадет на Сервер 2, и его "выкинет". Сессии уезжают в централизованное хранилище.

Уровень 4: Спасаем базу данных (500 000 юзеров)

Приложений много, а БД одна. Она начинает "задыхаться" от количества чтений.

• Что делаем (Кэш): Ставим Redis или Memcached. До 80% запросов в типичном приложении - это чтение одних и тех же данных. Кэш отдает их за миллисекунды.
• Что делаем (Репликация): Разделяем БД на Master (для записи) и несколько Slave/Replica (только для чтения).

Уровень 5: Асинхронность и Очереди (1 000 000+ юзеров)

Пользователи жалуются, что загрузка отчета или обработка видео занимает слишком много времени, а HTTP-соединения отваливаются по таймауту.

• Что делаем: Внедряем брокер сообщений (Kafka или RabbitMQ) и создаем воркеры.
• Как это работает: Юзер жмет "сгенерировать отчет". Приложение кидает задачу в Kafka и мгновенно отвечает юзеру: "В процессе". А фоновые серверы-воркеры не спеша забирают задачи из очереди и делают тяжелую работу.

🧠 Главный принцип System Design

Не усложняйте систему до тех пор, пока метрики не покажут, что текущий уровень больше не справляется. Каждое усложнение (Load Balancer, Redis, Kafka) несет за собой новые проблемы: инвалидация кэша, задержки сети, дублирование сообщений.

#SystemDesign #Architecture #Backend #Scaling

📲 Мы в MAX

👉 @java_geek
👍2🔥2❤1
В чем отличия между TreeSet и HashSet?

Основное отличие между TreeSet и HashSet заключается в том, что TreeSet хранит элементы в отсортированном порядке, в то время как порядок хранения элементов в HashSet не определен. TreeSet также обеспечивает быстрый поиск и извлечение элементов в отсортированном порядке.

📲 Мы в MAX

👉 @java_geek
👍2
Fluent Interface

Fluent Interface (Текучий интерфейс) — это шаблон проектирования, который позволяет создавать код, читающийся как текст на естественном языке.

Он достигается путем создания методов, которые возвращают ссылку на this объект, позволяя вызывать их в цепочке.

📲 Мы в MAX

👉 @java_geek
Что такое перегрузка методов в Java?

Перегрузка методов — это мощный приём, который позволяет классу иметь несколько методов с одинаковым именем, но разными параметрами. Это даёт объектам класса возможность обрабатывать близкие (почти одинаковые) задачи, но с разными входными данными.

Вот очень короткий пример, демонстрирующий перегрузку методов:


class Calculator {
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
}


В этом примере класс Calculator имеет два метода add:
один принимает два целых числа (int) и возвращает сумму в виде целого числа,
а другой — два числа с плавающей запятой (double) и возвращает сумму в виде double.

Имя метода одинаковое, но параметры различаются, что позволяет вызывать подходящий метод в зависимости от типов аргументов.

📲 Мы в MAX

👉 @java_geek
👍1
☕ Java Tip: Как работает var в Java

С версии Java 10 появился ключевое слово var. Оно упрощает код, автоматически выводя тип переменной:


var list = new ArrayList<String>();
list.add("Hello");
list.add("Java");

// Компилятор понимает, что list -> ArrayList<String>


✅ Удобно: меньше шаблонного кода.

❌ Важно: var не значит "динамическая типизация"! Тип фиксируется при компиляции.

📌 Используй var, если тип переменной очевиден - код станет чище.

📲 Мы в MAX

👉 @java_geek
👍1👎1🔥1
Какова цель ключевого слова final, когда оно используется с переменной?

Ключевое слово final в Java — это модификатор, который может применяться к переменным, методам и классам. Когда оно используется с переменной, это делает её неизменяемой, другими словами — константой. Например, переменная PI объявлена как final в классе ниже:


public class CircleCalculator {
private final double PI = 3.14159;
public double calculateArea(double radius) {
return PI * radius * radius;
}
}


📲 Мы в MAX

👉 @java_geek
👍1
Array vs ArrayList

Выбор между Array (стандартным Java-массивом) и ArrayList зависит от специфики задачи на Java, которую требуется решить. Помните о следующих особенностях этих типов:

☕️ Array имеет фиксированный размер и память для него выделяется в момент объявления, а размер ArrayList может динамически изменяться;
☕️ Массивы Java работают гораздо быстрее, а в ArrayList намного проще добавлять/удалять элементы;
☕️ При работе с Array велика вероятность получить ошибку ArrayIndexOutOfBoundsException;
☕️ У ArrayList только одно измерение, а вот массивы Java могут быть многомерными.

📲 Мы в MAX

👉 @java_geek
👍1
Как перебрать элементы LinkedList в обратном порядке, не используя медленный get(index)?

Для этого в LinkedList есть обратный итератор, который можно получить, вызвав метод descendingIterator().

📲 Мы в MAX

👉 @java_geek
👍2
Что такое стек-трейс?

Стек-трейс (stack trace) представляет собой список вызовов методов в обратном хронологическом порядке, начиная с метода, в котором произошло исключение. Стек-трейс позволяет отследить, какие методы были вызваны перед возникновением исключения, и предоставляет информацию о местоположении, где произошло исключение.

📲 Мы в MAX

👉 @java_geek
👍1