Java: fill the gaps
12.9K subscribers
7 photos
215 links
Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк

🔥Тот самый курс по многопочке🔥
https://fillthegaps.ru/mt

Комплименты, вопросы, предложения: @utki_letyat
Download Telegram
Программа курса

Напоминаю, что 19 апреля начинается курс многопоточки на Java. Часто получаю по нему такие вопросы:

Я прошёл курс Х и там была тема многопоточки. Для меня будет что-то новое?
Я сеньор, будут продвинутые темы? или всё только для джуниоров?

В этом посте отвечу на эти вопросы подробно.

Для кого?

Курс для тех, кто уже работает java разработчиком. Все проекты разные, поэтому ориентироваться на грейд не стоит. Кому подойдёт?
▫️ У вас есть начальные знания многопоточности, но вы редко решали задачи по этой теме
▫️ Отвечаете на собеседованиях про Thread, Executors, volatile и synchronized, но не сможете сходу рассказать про многопоточные коллекции и разницу между ними
▫️ Хотите на высоконагруженные проекты, и понимаете, что многопоточка там важна и нужна

Необходимые знания:
▫️ Умеете писать веб-сервисы на Spring, делать запросы в БД и писать юнит-тесты
▫️ Знаете паттерны GoF

Что будет?

У курса нетипичная структура. Классы java.util.concurrent идут не хронологически, а сгруппированы по области применения.

1️⃣ Вводный модуль

Что происходит с потоком в JVM и как на это влиять через флажки. Зачем нужна модель памяти, как она учитывается в коде. Как это всё мониторить и тестировать.

Затем углубимся в типовые задачи:

2️⃣ Запустить асинхронно независимые задачи

Что использовать и как подобрать параметры, чтобы выигрыш от параллельности превысил оверхед на организацию. Рассмотрим Executors со всех сторон. Посмотрим, когда пригодится CompletableFuture. Оценим перспективы легковесных потоков.

3️⃣ Взаимодействие потоков

Общие переменные или влияние одного потока на другой - узкое место любой реализации. JDK предлагает огромное количество инструментов. Разберёмся, что для какой ситуации подойдёт, возможные ошибки и лучшие практики.

Будем нещадно сравнивать классы между собой. У вас будет цельная картина, вы будете легко ориентироваться в возможных решениях. Список тем слишком длинный, поэтому просто оставлю ссылку на программу.

4️⃣ Обработка большого количества данных

Здесь инструмент один - ForkJoinPool. Начнём с принципа работы, закончим разбором кейсов. Посмотрим, как ForkJoinPool адаптирован для Stream API.

5️⃣ Потоки данных

Чем "много запросов" отличается от потока данных? Для каких случаев подойдёт и не подойдёт такой способ коммуникации? В этом модуле сосредоточимся на очередях и затронем тему реактивного программирования.

Детали реализации

В прошлом году я проводила опрос на тему многопоточки. Спрашивала у вас, какие темы более интересны и что хотелось бы узнать подробно.

Получила больше сотни(!) ответов и пожеланий. Понадобилось полгода, чтобы ответить на большинство вопросов😰 Зато курс пополнился чёткими рекомендациями и бенчмарками, стало больше реальных примеров и альтернатив за пределами JDK.

Что ещё

Будут практические задания, будет обратная связь. Группа маленькая, поэтому каждому достанется максимальная поддержка.

Записаться можно тут: fillthegaps.getcourse.ru/mt
👍2
В системе два микросервиса: А и Б.

У сервиса А есть метод GET /stat, который считает статистику. Для подсчёта используется внутренняя информация сервиса А и делается HTTP запрос GET /data в сервис Б.

Проект перевели на Java 11, и для доступа к сервису Б теперь используется асинхронный HTTP клиент. Стало значительно лучше, и менеджер проекта раздал всем премии.

Вопрос: Какая метрика улучшилась?
Как измерить выгоду от асинхронности?

Обычно у каждой задачи есть несколько решений. Надо обойти набор элементов? Меню JDK предлагает цикл do, do while, for в двух вариантах, Stream API. Выбирай, что удобно или привычно.

С многопоточкой такое не пройдёт. Здесь выбор инструмента влияет и на красоту кода, и на системные метрики. Расход памяти, задержки, скорость обработки и так далее.

Давайте разберём ситуацию в вопросе перед постом. Как выглядела картина ДО рефакторинга:

🔹 Сервис А в потоке X шлёт HTTP запрос в сервис Б.
🔹 Сервис Б принимает запрос, что-то считает и возвращает результат.
🔹 Пока Б развлекается, поток Х в сервисе А терпеливо ждёт.

Сколько (допустим) выполняется запрос /stat:
Логика в сервисе А - 100мс
Ожидание ответа сервиса Б - 500мс
Обработка ответа - 100мс

Итого: 700мс

В java 11 добавилась опция асинхронных HTTP запросов. Что стало с системой ПОСЛЕ рефакторинга:

🔹 Сервис А шлёт асинхронный HTTP запрос в сервис Б.
🔹 Сервис Б работает над запросом.
🔹 Поток в сервисе А в это время делает другие задачи.
🔹 Cервис Б заканчивает работу.
🔹 На сервисе А вызывается коллбэк, который обрабатывает ответ сервиса Б.

Что получаем:
Логика в сервисе А - 100мс
Ожидание ответа от сервиса Б - 500мс
Обработка ответа - 100мс

Итого: 700мс

Запрос /stat не стал быстрее.

Что изменилось?

Раньше поток сервиса А ждал ответ от Б и простаивал. Во время асинхронного запроса поток возвращается в планировщик и решает другие задачи.

Время выполнения запроса /stat не меняется, но общее количество работы, которое выполняет сервис А, увеличивается. Пропускная способность растёт: сервис обрабатывает больше запросов в секунду, чем раньше.

При небольшой нагрузке у сервиса может снизиться расход CPU. Но за такое редко выдают премии🙂

Так что правильный ответ на вопрос перед постом: увеличилась пропускная способность сервиса А.

Выводы

Иногда эффект многопоточных улучшений виден только под нагрузкой:
🔸 При работе с одним запросом разница может быть незаметна
🔸 Важно следить не только за кодом, но и за метриками
👍3
В классе Point 2 поля: Х и Y. Нужно реализовать два метода: update(x,y) и getX().

Класс должен быть потокобезопасным. Планируется, что читаться поля будут в 5 раз чаще, чем обновляться.

Есть три реализации - на основе synchronized, ReentrantLock и ReadWriteLock. Какую выберете?
👍2
Какую реализацию выберете?
Anonymous Poll
22%
synchronized
16%
ReentrantLock
63%
ReadWriteLock
👍2
Производительность synchronized и ReadWriteLock

Когда несколько переменных обновляются и читаются вместе, возникает опасность несогласованных изменений:

Был объект: точка (0, 0)
▫️Поток 1 хочет обновить координаты на (1, 1). Сначала обновляет X, потом Y.
▫️Поток 2 врывается в середине процесса и читает (0, 1).

Чтобы такого не было, используются критические секции: участки кода с ограниченным доступом. JDK предлагает 5 вариантов организовать критическую секцию:
🔸 synchronized
🔸 ReentrantLock
🔸 ReentrantReadWriteLock
🔸 StampedLock
🔸 Semaphore

Три первых класса встречаются чаще всего, поэтому сравним их между собой.

synchronized и ReentrantLock дают эксклюзивный доступ потока к коду. Чтение объекта никогда не происходит одновременно с обновлением.

ReadWriteLock предлагает такую идею: если переменная сейчас не обновляется, то нет смысла ограничивать количество читающих потоков. Для этого в ReadWriteLock есть два вида локов:
🔹 Для чтения: readLock()
🔹 Для записи: writeLock()

Выбираем реализацию

Известно, что переменные читаются в 5 раз чаще, чем обновляются.
▫️ ReetrantLock и synchronized пускают только один поток в критическую секцию
▫️ ReadWriteLock запускает несколько потоков на чтение

Делаем прогноз, что ReadWriteLock разгромно победит остальные варианты.

Эксперимент

Измерим пропускную способность каждой реализации. Попробуем разную нагрузку и соотношение чтения-записи. Результаты сведём в график.

По горизонтали отметим разные кейсы. 5-1 означает, что в один момент 5 потоков читают значения, и 1 поток обновляет переменную.

По вертикали - пропускная способность. Чем выше график, тем лучше

Сам график - внизу поста⬇️

Результаты

Шок-контент! synchronized и ReetrantLock работают в 2-3 раза лучше, чем ReadWriteLock. Только в ситуации 10-1 его скорость немного приближается к конкурентам.

Почему ReadWriteLock проиграл?

Если кратко - сложная и неоптимальная реализация. ReetrantLock и synchronized используют простые конструкции и выигрывают у специализированного ReadWriteLock.

Логичен такой результат? Нет
Очевиден из чтения документации? Нет

Подобных ситуаций в JDK не так много. Большинство классов хорошо спроектированы, и при правильном использовании работают отлично.
————————
На курс многопоточки осталось 3 места. Старт уже в следующий понедельник.
👍2
Ребята, последние места разобрали как пирожки🥐

Вы можете оставить свои контакты, тогда вам в числе первых придёт письмо про новый набор: https://fillthegaps.getcourse.ru/mtList
Зачем нужны микрофреймворки?

Последние пару лет на конференциях часто обсуждают микрофреймворки и активно сравнивают их со Spring.

Joker 2019: Micronaut vs Spring Boot
Joker 2020: Spring: Your next Java microframework

Спикеры показывают, как легко на микрофреймворках создать сервис из 3х классов, и как мало памяти он потребляет. Как Spring Boot приложение стартует за одну секунду, если выкинуть из него всё полезное.

Экономия 50 МБ - это здорово, но не настолько, чтобы срочно менять фреймворк. В этом посте я расскажу, в каких случаях полезны микрофреймворки и что означает приставка "микро".

🌚 Перенесёмся на 10 лет назад

Компания ведёт деятельность в интернете. У неё несколько веб-сервисов, допустим на Spring. В компаниях есть "серверные" - большие комнаты с кучей оборудования. На них развёрнуты приложения, готовые к наплыву пользователей. Даже когда пользователей мало, сервера стоят в полной готовности. Экземпляр сервиса запущен неделями, а релиз - волнительное для всех событие.

🌝 Наши дни

Серверные освободились от железок и стали комнатами отдыха. Приложения запускаются на сторонних сервисах. Это удобно - можно быстро поднять и уничтожить любое количество экземпляров, а платить только за использованные ресурсы. Мало пользователей - мало сервисов. Нагрузка растёт - добавляем ещё.

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

Классическое приложение на Spring стартует больше минуты. Пока запускается, помощь уже может и не понадобиться.

Проблему долгого старта решают микрофреймворки.

Микро - потому что для микросервисов. Чёткой границы между сервисом и микросервисом нет, так же функционально нет чёткой границы между фреймворком и микрофреймворком. Это полноценные приложения, которые могут и запросы принять, и к БД обратиться, и провернуть сложную бизнес-логику.

Ключевое отличие - это быстрый старт сервиса🚀

Магия фреймворков скрыта за аннотациями. По ним фреймворк создаёт классы-обёртки, в которых спрятан скучный код. Прокси классы создаются во время компиляции (микрофреймворки) или во время запуска программы (Spring). Понятно, что если классы созданы заранее, то приложение стартует гораздо быстрее.

Подробно об аннотациях и Retention можно почитать в этом посте.

Аннотации микрофреймворков очень похожи на аннотации Spring. Например, o.s.w.b.a.RestController для Micronaut превращается в i.m.h.a.Controller(produces = [TEXT_JSON]). Микрофреймворки активно развиваются и легко интегрируются с популярными 3rd parties.

Но перейти с одного фреймворка на другой - это не шутка. Как минимум надо переписать все аннотации и терпеть долгую компиляцию. Некоторые проекты даже на java 11 не обновляются, а смена фреймворка тем более должна иметь серьёзное обоснование. Если ваш проект не дошёл до стадии, когда сервисы запускаются по необходимости, то можно остаться на старом добром Spring.
👍3
11 шагов к Senior Google User

Главный навык разработчика - умение пользоваться интернетом. Чтобы занять голову не регулярками и опциями докера, а действительно важными вещами. Вот несколько приёмов для быстрого и чёткого поиска.

1️⃣ Искать по-английски
Материала больше, обычно он приятнее и понятнее русских аналогов.

2️⃣ Кавычки для поиска конкретной фразы
"parse pdf to xml"

Чтобы пропустить часть фразы, используйте звёздочку:
"java regex * tutorial"

3️⃣ AND
Обязательное вхождение обоих слов / фраз

"Spring in Action" AND "Евгений Борисов"

4️⃣ OR
Для поиска одного из вариантов

"how to do java" OR "java for beginners"

5️⃣ Исключить ключевое слово через минус
java types -javascript

Работает и для сайтов:
как пройти собеседование -otvet.mail.ru

6️⃣ Поиск на конкретном сайте
NPE site:stackoverflow.com

7️⃣ Найти файлы
Удобно искать книжки, презентации к докладам

"effective java" filetype:pdf

8️⃣ Найти только свежие / старые материалы
thread safety before:2020
functional programming after:2020-12-20

9️⃣ Быстрый перевод
coherence in russian
resilience по русски

🔟 Сложный поиск
Чтобы не запоминать эти приёмчики, есть страница Advanced Search с формочками для всех критериев и опцией "Скрывать непристойные результаты".

🔸 Бонусный пункт для владельцев Android со связанным Google аккаунтом:

find my phone в строке поиска. Даже если звук на телефоне выключен, можно включить звонок и найти телефон.
👍3
Закулисье курса по многопоточке

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

О чём вообще речь.

В прошлом году я начала готовить курс по многопоточке на java. Тема важная и сложная, а хороших материалов очень мало. Получился двухмесячный концентрированный курс: уроки с заданиями выходят три раза в неделю. Три недели назад стартовал первый поток, поэтому можно уже поделиться результатами.

По цифрам:
🔹 Участники с опытом от года до 8 лет
🔹 33% выполняют задания к уроку в течение 2 дней
🔹 60% - в течение недели
🔹 Самая активное время сдачи ДЗ - с 22 до часу ночи🌚
🔹 1 человеку не подошёл формат, вернула деньги

Что говорят ребята:
❤️ Курс действительно заполняет пробелы. Интересна точка зрения опытного разработчика на проблемы
❤️ Большое спасибо, что вы находите время писать развернутые комментарии и отвечать на смежные вопросы!) Это круто)
❤️ Если говорить в целом - то я очень доволен курсом
❤️ Сложно конечно, много нового для меня, не всегда успеваю сразу сдавать. Но чувствую, что полезно))

В целом я довольна. Времени на обратную связь трачу много, но, кажется, не зря.

Поэтому в июле планирую повторить. Тоже с небольшой группой, не больше 20 учеников. В предварительном списке уже больше 40 человек! Если тоже хотите первыми узнать дату старта и условия, вписывайтесь сюда:

Предварительный список на двухмесячный курс по java.util.concurrent и смежным вопросам
Собеседование:
- Расскажите про enumerations.
- Ну, это набор констант
- Как их можно использовать в коде?
- Как набор констант
(что им надо?)
- А как они реализованы в JVM?
- Как константы
(спросите лучше про хэшмэп)

Чтобы не получилось такой ситуации, на этой неделе разберёмся с темой перечислений. Enum - очень полезные конструкции. С ними код становится проще и безопаснее. Если их использовать, конечно.

План такой:
Часть 1 - обзор enumerations
Часть 2 - как используется на практике
Часть 3 - разберём загрузку классов и душный вопрос с собеседования
В enum Planet есть метод getRadius. В классе Meteor тоже он есть. Мы хотим сделать метод getInfo, который работает с обеими сущностями и вызывает их метод getRadius.

Вопрос: что будет вместо солнышка во входном параметре?
(щёлкните по картинке, чтобы открыть полностью)
Enumerations, часть 1: обзор

Внутри JVM нет такого понятия как enum. Енумы компилируются в обычный класс, а значения - в статические экземпляры:

public final class Planet extends Enum {
public static final Planet EARTH;
public static final Planet MARS;
}

Полноценные классы c конструкторами, методами, полями и статическими элементами. У экземпляров есть состояние и определённое поведение.

Но есть нюансы:

🔸 Суперкласс Enum

От суперкласса Enum наследуются методы name(), ordinal() и статический метод values().
name() возвращает имя переменной, ordinal() - порядковый номер в списке. На практике эти методы достаточно бесполезны.

Метод values() используется чаще и возвращает массив всех объектов. Можно пройтись по нему в цикле:
for (Planet p : Planet.values()) {…}

или через Stream API:
Arrays.stream(Planet.values()).forEach(…)

🔸 Интерфейсы суперкласса

Enum реализует три интерфейса: Comparable, Serializable, Constable.

Первые два всем знакомы. Интерфейс Constable определяет методы для размещения объектов в пуле констант внутри JVM.

🔸 Создание экземпляров

Этим занимается JVM на старте приложения. Экземпляры енума создаются через приватный конструктор, недоступный вне енума.

🔸 Поля

Указываются для каждого экземпляра, инициализируются в конcтрукторе:

public enum Planet {
MARS(3389),
EARTH(6371);
int radius;

Planet(int radius) {
this.radius = radius;
}
}

🔸 Два типа методов

▫️Обычные
public int getRadius()

Используется для геттеров и простых вычислений.

В теории можно сделать set* метод и поменять поле у любого экземпляра. Но на практике так никто не делает. Когда объект доступен из любого места системы, то проще жить, если он неизменяемый.

▫️Абстрактные
Каждый экземпляр определяет свою реализацию:

public enum Planet {
MARS {
int distanceFrom(int) {…}
},
EARTH {
int distanceFrom(int) {…}
};

abstract int distanceFrom(int);
}

🔸 Наследование

Любой enum - это final класс с уже определённым суперклассом. Единственный шанс встроить enum в иерархию - добавить для него интерфейс.

Иногда это удобно. Если у енумов и классов один интерфейс, то с ними можно работать через один метод:

interface SpaceObject

enum Planet implements SpaceObject
class Meteor implements SpaceObject

public void getInfo(SpaceObject so)
👍9🔥1
Вопрос
Enum Formatter занимается форматированием строк в заданные форматы. Во время работы было выполнено 50 форматирований в XML, в JSON - 30, в YAML - 5.

Что вернёт метод Formatter.JSON.getCount()?
Enumerations, часть 2: практика

В прошлом посте разобрали, из чего сделан enum. В этом - разберёмся, как его использовать.

1️⃣ Набор констант

Самый простой и популярный случай. Используем enum как поле класса или локальную переменную. Проверяем значение через if / switch и делаем ветвления в коде:

if (state == UserState.NEW) {…}

2️⃣ Паттерн Singleton

Вообще синглтон - антипаттерн и антитренд. Если без него никак - рассмотрите вариант с enum. Если вы склонны к риску, можно сделать изменяемые поля.

Может быть несколько объектов в рамках одного enum
Доступ из любого места в коде
Ленивая инициализация

3️⃣ Паттерн State

Более продвинутый вариант, чем "набор констант". Здесь фокус на данных: используются связанные значения, а не элемент enum сам по себе.

Пример: в статус пользователя вносим дополнительные поля - необходимость подтверждения личности и максимальный размер скидки:

enum UserState { 
NEW(true, 10),
VALIDATED(false, 25),
FRAUD(true, 0);

boolean needConfirmation;
int maxDiscount;
}

4️⃣ Паттерн Strategy

Здесь фокус на разнице в поведении. Абстрактный метод переопределяется для каждого экземпляра:

NEW { boolean check(Order){…}},
VALIDATED { boolean check(Order){…}};

abstract boolean check(Order order)

Подойдёт для алгоритмов, конвертеров, сортировок
Простая иерархия, всё в одном классе
Список всех значений доступен через values()
😐 Весь код в одном файле. Не всегда удобно
Сложно связать с другими объектами
Нарушается принцип open for extension but closed for modification

Получается специфическая альтернатива полиморфизму, но иногда ок.

Enum - не просто набор констант. Инструмент своеобразный, но полезно знать и такие реализации паттернов. Чем шире арсенал разработчика, тем выше шанс найти подходящее решение.
👍51
Вопрос
В классе User хранится статус в виде enum State.

В каком порядке и как будут создаваться объекты при первом вызове User user = new User()?