Как реализовать паттерн producer/consumer?
Шаблон producer/consumer (производитель/потребитель) – простая и базовая реализация обмена данными между несколькими потоками. Поток-производитель отправляет объекты на условную обработку, потоки-потребители асинхронно принимают и обрабатывают их.
Общий вид решения выглядит так. Продюсер отправляет объекты в специальную коллекцию – буфер. Когда потребитель освобождается, он отправляет запрос на извлечение одного объекта из буфера. Если буфер пуст, потребитель блокируется и ждет, если буфер переполнен – ждет производитель.
На практике реализовать этот паттерн можно множеством способов. Самый правильный способ для применения в бою – использовать готовую реализацию из стандартной библиотеки, объект типа BlockingQueue.
На собеседовании обычно просят реализовать паттерн с нуля. Реализация представлена на изображении. Модификатор synchronized делает так, чтобы в каждый момент времени мог выполняться только один из методов, и только одним потоком. Этого достаточно для корректной работы пока буфер не пуст и не полон. При пустом или полном буфере управление явно перебрасывается на производителя или потребителя соответственно, с помощью методов notify() и wait().
Шаблону producer/consumer посвящена глава 5.3 книги Java Concurrency in Practice.
Сильно упрощая, на основе этого паттерна работают сервисы-брокеры сообщений: Rabbit MQ, Apache ActiveMQ и другие.
Java Guru🤓 #java
Шаблон producer/consumer (производитель/потребитель) – простая и базовая реализация обмена данными между несколькими потоками. Поток-производитель отправляет объекты на условную обработку, потоки-потребители асинхронно принимают и обрабатывают их.
Общий вид решения выглядит так. Продюсер отправляет объекты в специальную коллекцию – буфер. Когда потребитель освобождается, он отправляет запрос на извлечение одного объекта из буфера. Если буфер пуст, потребитель блокируется и ждет, если буфер переполнен – ждет производитель.
На практике реализовать этот паттерн можно множеством способов. Самый правильный способ для применения в бою – использовать готовую реализацию из стандартной библиотеки, объект типа BlockingQueue.
На собеседовании обычно просят реализовать паттерн с нуля. Реализация представлена на изображении. Модификатор synchronized делает так, чтобы в каждый момент времени мог выполняться только один из методов, и только одним потоком. Этого достаточно для корректной работы пока буфер не пуст и не полон. При пустом или полном буфере управление явно перебрасывается на производителя или потребителя соответственно, с помощью методов notify() и wait().
Шаблону producer/consumer посвящена глава 5.3 книги Java Concurrency in Practice.
Сильно упрощая, на основе этого паттерна работают сервисы-брокеры сообщений: Rabbit MQ, Apache ActiveMQ и другие.
Java Guru🤓 #java
🔥11👍6❤3
🗓 17 июля в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Java-разработчик».
Ошибки в кодировках ломают приложения, превращая текст в «абракадабру». Понимание таблиц кодировок — must-have навык для работы с данными, файлами и международными проектами.
О чём поговорим:
Кому будет интересно:
Начинающим Java-разработчикам и тестировщикам, сталкивающимся с международными данными и проблемами кодировки.
В результате вебинара вы:
Научитесь контролировать кодировки в своих приложениях, исправлять «кракозябры» и работать с любыми текстовыми данными без страха.
🔗 Ссылка на регистрацию: https://vk.cc/cNvfEE
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3👍2🔥2
Что такое ForkJoinPool?
ForkJoinPool – специальный вид ExecutorService (пулла потоков), который появился в Java с версии 7. Предназначен для выполнения рекурсивных задач.
Задача для сервиса представляется экземпляром класса ForkJoinTask. В основном используются подклассы RecursiveTask и RecursiveAction, для задач с результатом и без соответственно. Аналогично интерфейсам Callable и Runnable обычного ExecutorService.
Тело рекурсивной операции задается в реализации метода compute() задачи ForkJoinTask. Здесь же создаются новые подзадачи, и запускаются параллельно методом fork(). Чтобы дождаться завершения выполнения задачи, на каждой форкнутой подзадаче вызывается блокирующий метод join(), результат выполнения при необходимости агрегируется.
С точки зрения использования метод ForkJoinTask.join() похож на аналогичный метод класса Thread. Но в случае fork-join поток может на самом деле не заснуть, а переключиться на выполнение другой задачи. Такая стратегия называется work stealing, и позволяет эффективнее использовать ограниченное количество потоков. Это похоже на переиспользование потоков корутинах Kotlin (green threads).
Примеры практического использования ForkJoinPool.
Java Guru🤓 #java
ForkJoinPool – специальный вид ExecutorService (пулла потоков), который появился в Java с версии 7. Предназначен для выполнения рекурсивных задач.
Задача для сервиса представляется экземпляром класса ForkJoinTask. В основном используются подклассы RecursiveTask и RecursiveAction, для задач с результатом и без соответственно. Аналогично интерфейсам Callable и Runnable обычного ExecutorService.
Тело рекурсивной операции задается в реализации метода compute() задачи ForkJoinTask. Здесь же создаются новые подзадачи, и запускаются параллельно методом fork(). Чтобы дождаться завершения выполнения задачи, на каждой форкнутой подзадаче вызывается блокирующий метод join(), результат выполнения при необходимости агрегируется.
С точки зрения использования метод ForkJoinTask.join() похож на аналогичный метод класса Thread. Но в случае fork-join поток может на самом деле не заснуть, а переключиться на выполнение другой задачи. Такая стратегия называется work stealing, и позволяет эффективнее использовать ограниченное количество потоков. Это похоже на переиспользование потоков корутинах Kotlin (green threads).
Примеры практического использования ForkJoinPool.
Java Guru🤓 #java
👍9🔥5❤4
Чем ForkJoinPool отличается от ExecutorService?
ForkJoinPool сам по себе является наследником ExecutorService. Вопрос подразумевает его отличия от обычного пула потоков – ThreadPoolExecutor.
Преимущества, которые дает work stealing по сравнению с обычным пулом:
• Сокращение расходов на переключение контекста;
• Защита от проблемы голодания потоков (thread starvation);
• Защита от дедлока для рекурсивных задач.
Как положено любому представителю ExecutorService, ForkJoinPool тоже умеет выполнять Runnable и Callable, но помимо этого работает и со специальными задачами ForkJoinTask, о которых также говорилось ранее.
Интерфейс настройки и мониторинга остается тем же, что и в классических тред-пулах.
Каждый обычный пул использует собственный набор потоков. ForkJoinPool по умолчанию использует общий пул-синглтон commonPool. Альтернативный отдельный пул всё еще можно задать в конструкторе.
ForkJoinPool сам регулирует количество запущенных потоков, достигая максимальной эффективности при заданном уровне параллелизма.
Java Guru🤓 #java
ForkJoinPool сам по себе является наследником ExecutorService. Вопрос подразумевает его отличия от обычного пула потоков – ThreadPoolExecutor.
Преимущества, которые дает work stealing по сравнению с обычным пулом:
• Сокращение расходов на переключение контекста;
• Защита от проблемы голодания потоков (thread starvation);
• Защита от дедлока для рекурсивных задач.
Как положено любому представителю ExecutorService, ForkJoinPool тоже умеет выполнять Runnable и Callable, но помимо этого работает и со специальными задачами ForkJoinTask, о которых также говорилось ранее.
Интерфейс настройки и мониторинга остается тем же, что и в классических тред-пулах.
Каждый обычный пул использует собственный набор потоков. ForkJoinPool по умолчанию использует общий пул-синглтон commonPool. Альтернативный отдельный пул всё еще можно задать в конструкторе.
ForkJoinPool сам регулирует количество запущенных потоков, достигая максимальной эффективности при заданном уровне параллелизма.
Java Guru🤓 #java
👍9🔥4❤2
15 июля в 20:00 МСК OTUS проведёт открытый урок «Нормальная денормализация» — ключевой приём для оптимизации доступа к данным в NoSQL.
На примере Spring Data MongoDB разберём, как настраивать связи между сущностями: когда выбрать вложенные документы, а когда — ссылочные связи. Вы поймёте, как денормализация влияет на производительность запросов, расходы на память и сложность поддержки.
Урок будет полезен Java-разработчикам, backend-инженерам и архитекторам, работающим с MongoDB. Вы получите готовые шаблоны организации данных в Spring-приложениях, избежите типичных ошибок при проектировании схемы и сможете принимать обоснованные архитектурные решения.
Присоединяйтесь к уроку и получите скидку на полный курс «Разработчик на Spring Framework».
Регистрируйтесь прямо сейчас: https://vk.cc/cNzFo6
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
На примере Spring Data MongoDB разберём, как настраивать связи между сущностями: когда выбрать вложенные документы, а когда — ссылочные связи. Вы поймёте, как денормализация влияет на производительность запросов, расходы на память и сложность поддержки.
Урок будет полезен Java-разработчикам, backend-инженерам и архитекторам, работающим с MongoDB. Вы получите готовые шаблоны организации данных в Spring-приложениях, избежите типичных ошибок при проектировании схемы и сможете принимать обоснованные архитектурные решения.
Присоединяйтесь к уроку и получите скидку на полный курс «Разработчик на Spring Framework».
Регистрируйтесь прямо сейчас: https://vk.cc/cNzFo6
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
👍3🔥3❤2
Что будет результатом кода?
Anonymous Quiz
4%
0
7%
1
50%
2
8%
Ошибка компиляции
31%
Невозможно предсказать
1👍8🔥5
LangChain открывает мощные возможности LLM в приложениях Java, упрощая интеграцию ИИ в сервисы на Quarkus и Spring Boot.
🗓 17 июня в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Java Developer. Advanced».
- Framework LangChain: ключевые концепции и архитектура.
- Langchain4j: возможности Java-библиотеки.
- Интеграция Langchain4j в проекты на Spring Boot и Quarkus.
- Spring AI: фреймворк для работы с LLM в Spring.
Java-разработчикам, архитекторам ПО и инженерам ML Ops, планирующим внедрять LLM в микросервисы на Quarkus или Spring Boot.
Познакомитесь с Langchain4j и сможете написать простой Java-сервис, использующий LLM.
🔗 Ссылка на регистрацию: https://vk.cc/cNzVUK
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
1❤3🔥3👍2
Что будет результатом кода?
Anonymous Quiz
18%
10 целых случайных чисел от 1 до 10
10%
10 целых одинаковых случайных чисел
7%
10 дробных случайных чисел
51%
10 целых случайных чисел
13%
ошибка компиляции
👍11🔥4❤3
Как работают параллельные стримы?
Основная цель, ради которой в Java 8 был добавлен Stream API – удобство многопоточной обработки.
Обычный стрим будет выполняться параллельно после вызова промежуточной операции parallel(). Некоторые стримы создаются уже многопоточными, например результат вызова Collection#parallelStream(). Для распараллеливания используется единый общий ForkJoinPool.
Внутри реализации потока его сплиттератор оборачивается в AbstractTask, который и отправляется на выполнение в пул. AbstractTask при выполнении считывает estimateSize сплиттератора и текущую степень параллелизма пула. На основе этих данных он принимает решение, распараллелить ли сплиттератор на два методом trySplit().
У удобства такого решения есть обратная сторона. Так как пул единый, нагрузка распределяется на всех пользователей параллельных стримов в программе. Если в одном потоке выполняются долгие блокирующие операции, это может ударить по производительности в совершенно не связанном с ним другом потоке.
Если всё же требуется использовать отдельный пул потоков, сам стрим выполняется как задача этого отдельного пула.
Java Guru🤓 #java
Основная цель, ради которой в Java 8 был добавлен Stream API – удобство многопоточной обработки.
Обычный стрим будет выполняться параллельно после вызова промежуточной операции parallel(). Некоторые стримы создаются уже многопоточными, например результат вызова Collection#parallelStream(). Для распараллеливания используется единый общий ForkJoinPool.
Внутри реализации потока его сплиттератор оборачивается в AbstractTask, который и отправляется на выполнение в пул. AbstractTask при выполнении считывает estimateSize сплиттератора и текущую степень параллелизма пула. На основе этих данных он принимает решение, распараллелить ли сплиттератор на два методом trySplit().
У удобства такого решения есть обратная сторона. Так как пул единый, нагрузка распределяется на всех пользователей параллельных стримов в программе. Если в одном потоке выполняются долгие блокирующие операции, это может ударить по производительности в совершенно не связанном с ним другом потоке.
Если всё же требуется использовать отдельный пул потоков, сам стрим выполняется как задача этого отдельного пула.
Java Guru🤓 #java
👍10🔥5
🔥 БЕСПЛАТНЫЙ КУРС ПО СОЗДАНИЮ НЕЙРО-СОТРУДНИКОВ НА GPT И ДРУГИХ LLM 🔥
Ищете практический и углубленный курс, чтобы освоить создание нейро-сотрудников? Мы создали курс из 5 объемных занятий. Это именно то, что нужно, чтобы прокачать свои навыки абсолютно бесплатно!
📌 Темы занятий:
1. Введение в мир нейро-сотрудников
2. Как работают LLM и их аналоги
3. Создание базы знаний для нейро-сотрудника (RAG)
4. Тестирование и отладка нейро-сотрудников
5. Интеграция нейро-сотрудников в Production
Вот 5 тем курса - он максимально простой и доступный, общеобразовательный, без какого-либо сложного программирования 📚Прохождение этого курса, скорее всего, займет у вас от 1 до 3 часов
🤖 Присоединяйтесь к нашему бесплатному курсу и разберитесь в этой увлекательной теме с нами!
Ищете практический и углубленный курс, чтобы освоить создание нейро-сотрудников? Мы создали курс из 5 объемных занятий. Это именно то, что нужно, чтобы прокачать свои навыки абсолютно бесплатно!
📌 Темы занятий:
1. Введение в мир нейро-сотрудников
2. Как работают LLM и их аналоги
3. Создание базы знаний для нейро-сотрудника (RAG)
4. Тестирование и отладка нейро-сотрудников
5. Интеграция нейро-сотрудников в Production
Вот 5 тем курса - он максимально простой и доступный, общеобразовательный, без какого-либо сложного программирования 📚Прохождение этого курса, скорее всего, займет у вас от 1 до 3 часов
🤖 Присоединяйтесь к нашему бесплатному курсу и разберитесь в этой увлекательной теме с нами!
👍3❤1
Чем CompletableFuture отличается от Future?
Future – интерфейс, который представляет пока еще недовычисленный результат. Когда породившая его асинхронная операция заканчивается, он заполняется значением. Метод get блокирует выполнение до получения результата, isDone проверяет его наличие. К примеру результат выполнения задач в ExecutorService, ForkJoinTask, реализует интерфейс Future.
CompletableFuture появился в Java 8. Это класс-реализация старого интерфейса Future, а значит всё сказанное выше справедливо и для него. Вдобавок к этому, CompletableFuture реализует работу с отложенными результатами посредством коллбэков. Метод thenApply регистрирует код обработки значения, который будет автоматически вызван позже, когда это значение появится.
В Java 9 прогресс пошел дальше, и появилась библиотека Flow API. Это встроенная реализация реактивных стримов. Реактивный стрим, сильно упрощая, – это более общий случай, последовательность отложенных значений. Другая их реализация – популярная, но не входящая в стандарт библиотека Reactive Extensions (RxJava).
Java Guru🤓 #java
Future – интерфейс, который представляет пока еще недовычисленный результат. Когда породившая его асинхронная операция заканчивается, он заполняется значением. Метод get блокирует выполнение до получения результата, isDone проверяет его наличие. К примеру результат выполнения задач в ExecutorService, ForkJoinTask, реализует интерфейс Future.
CompletableFuture появился в Java 8. Это класс-реализация старого интерфейса Future, а значит всё сказанное выше справедливо и для него. Вдобавок к этому, CompletableFuture реализует работу с отложенными результатами посредством коллбэков. Метод thenApply регистрирует код обработки значения, который будет автоматически вызван позже, когда это значение появится.
В Java 9 прогресс пошел дальше, и появилась библиотека Flow API. Это встроенная реализация реактивных стримов. Реактивный стрим, сильно упрощая, – это более общий случай, последовательность отложенных значений. Другая их реализация – популярная, но не входящая в стандарт библиотека Reactive Extensions (RxJava).
Java Guru🤓 #java
🔥12👍6
Зачем выбирать ReentrantLock вместо synchronized?
Объект класса ReentrantLock решает те же задачи, что и блок synchronized. Поток висит на вызове метода lock() в ожидании своей очереди занять этот объект. Владеть локом, как и находиться внутри блока synchronized может только один поток одновременно. unlock(), подобно выходу из блока синхронизации, освобождает объект-монитор для других потоков.
В отличие от блока синхронизации, ReentrantLock дает расширенный интерфейс для получения информации о состоянии блокировки. Методы лока позволяют еще до блокировки узнать, занят ли он сейчас, сколько потоков ждут его в очереди, сколько раз подряд текущий поток завладел им.
Шире и возможные режимы блокировки. Кроме обычного ожидающего lock(), вариант tryLock() с параметром ожидает своей очереди только заданное время, а без параметра – вообще не ждет, а только захватывает свободный лок.
Еще одно отличие – свойство fair. Лок с этим свойством обеспечивает «справедливость» очереди: пришедший раньше поток захватывает объект раньше. Блок synchronized не дает никаких гарантий порядка.
Java Guru🤓 #java
Объект класса ReentrantLock решает те же задачи, что и блок synchronized. Поток висит на вызове метода lock() в ожидании своей очереди занять этот объект. Владеть локом, как и находиться внутри блока synchronized может только один поток одновременно. unlock(), подобно выходу из блока синхронизации, освобождает объект-монитор для других потоков.
В отличие от блока синхронизации, ReentrantLock дает расширенный интерфейс для получения информации о состоянии блокировки. Методы лока позволяют еще до блокировки узнать, занят ли он сейчас, сколько потоков ждут его в очереди, сколько раз подряд текущий поток завладел им.
Шире и возможные режимы блокировки. Кроме обычного ожидающего lock(), вариант tryLock() с параметром ожидает своей очереди только заданное время, а без параметра – вообще не ждет, а только захватывает свободный лок.
Еще одно отличие – свойство fair. Лок с этим свойством обеспечивает «справедливость» очереди: пришедший раньше поток захватывает объект раньше. Блок synchronized не дает никаких гарантий порядка.
Java Guru🤓 #java
👍13🔥6❤2
Что произойдет при запуске?
Anonymous Quiz
5%
Исключение из try будет обработано, и блок finally не выполнится
59%
Исключение finally перезапишет исключение try, выведется: "Exception: Exception in finally block".
25%
Оба исключения будут напечатаны, сначала исключение из try, потом из finally
3%
Программа завершится с двумя неперехваченными исключениями
9%
Исключение try выбросится первое, выведется: "Exception in try block".
1👍10🔥3
Как используется метод Lock.newCondition()?
Если реализации интерфейса Lock представляют высокоуровневую альтернативу блока synchronized, то реализации его спутника, интерфейса Condition – альтернатива методам notify/wait. Оба этих интерфейса относятся к пакету java.util.concurrent.locks.
Как и ожидание на мониторе, Condition реализует примитив синхронизации «Условная переменная». Один или несколько потоков зависают на объекте-кондишне с помощью варианта метода await (ждут удовлетворения условия). Другой поток пробуждает их методами signal и signalAll (сигнализирует об удовлетворении условия).
Конкретные реализации Condition всегда решают те же задачи, что блокировка на мониторе, но в теории могут отличаться в нюансах поведения. Например, может не быть требования вызывать ожидание/сигнал только при захваченном локе (аналог требования, по которому notify/wait всегда вызываются в synchronized). Или может гарантироваться порядок получения сигнала ожидающими потоками.
Возвращаясь к поставленному вопросу, Condition всегда связан со своим объектом типа Lock, и метод Lock.newCondition() – единственный правильный способ создания кондишна.
Java Guru🤓 #java
Если реализации интерфейса Lock представляют высокоуровневую альтернативу блока synchronized, то реализации его спутника, интерфейса Condition – альтернатива методам notify/wait. Оба этих интерфейса относятся к пакету java.util.concurrent.locks.
Как и ожидание на мониторе, Condition реализует примитив синхронизации «Условная переменная». Один или несколько потоков зависают на объекте-кондишне с помощью варианта метода await (ждут удовлетворения условия). Другой поток пробуждает их методами signal и signalAll (сигнализирует об удовлетворении условия).
Конкретные реализации Condition всегда решают те же задачи, что блокировка на мониторе, но в теории могут отличаться в нюансах поведения. Например, может не быть требования вызывать ожидание/сигнал только при захваченном локе (аналог требования, по которому notify/wait всегда вызываются в synchronized). Или может гарантироваться порядок получения сигнала ожидающими потоками.
Возвращаясь к поставленному вопросу, Condition всегда связан со своим объектом типа Lock, и метод Lock.newCondition() – единственный правильный способ создания кондишна.
Java Guru🤓 #java
👍4❤2🔥2
Чем отличается ReentrantLock от обычного Lock?
Lock – это интерфейс, ReentrantLock – его реализация. «Reentrant» говорит о том, что один и тот же поток может перезахватывать уже захваченный лок. Интерфейс не требует этого свойства. Обычный блок synchronized тоже является reentrant – вложенная синхронизация на том же мониторе отработает без проблем.
Примеры не-reentrant локов из стандартной библиотеки – представления класса StampedLock, возвращаемые его методами asReadLock() и asWriteLock().
Java Guru🤓 #java
Lock – это интерфейс, ReentrantLock – его реализация. «Reentrant» говорит о том, что один и тот же поток может перезахватывать уже захваченный лок. Интерфейс не требует этого свойства. Обычный блок synchronized тоже является reentrant – вложенная синхронизация на том же мониторе отработает без проблем.
Примеры не-reentrant локов из стандартной библиотеки – представления класса StampedLock, возвращаемые его методами asReadLock() и asWriteLock().
Java Guru🤓 #java
🔥6👍2
Как использовать ReadWriteLock?
Стандартный интерфейс ReadWriteLock предоставляет потокобезопасный разделенный доступ на чтение и на запись. Для этих целей в нём объявлены два метода: readLock() и writeLock(). Они возвращают объекты под интерфейсом Lock.
Оба типа блокировок одного экземпляра ReadWriteLock связаны. Пока какой-то поток не заберет блокировку на запись, сколько угодно потоков могут читать не мешая друг другу. Блокировкой readLock закрывается часть кода с семантикой «только чтения» некоторого условного «ресурса». В критической секции кода writeLock осуществляется модификация ресурса.
Свойства этих локов защищают программу от ситуаций конкурентной записи ресурса и чтения во время записи. Подобно copy-on-write коллекциям, этот подход становится выгодным, когда ресурс читают сильно чаще чем модифицируют.
Интерфейс реализуется классом ReentrantReadWriteLock, который во многом похож на обычный ReentrantLock.
Java Guru🤓 #java
Стандартный интерфейс ReadWriteLock предоставляет потокобезопасный разделенный доступ на чтение и на запись. Для этих целей в нём объявлены два метода: readLock() и writeLock(). Они возвращают объекты под интерфейсом Lock.
Оба типа блокировок одного экземпляра ReadWriteLock связаны. Пока какой-то поток не заберет блокировку на запись, сколько угодно потоков могут читать не мешая друг другу. Блокировкой readLock закрывается часть кода с семантикой «только чтения» некоторого условного «ресурса». В критической секции кода writeLock осуществляется модификация ресурса.
Свойства этих локов защищают программу от ситуаций конкурентной записи ресурса и чтения во время записи. Подобно copy-on-write коллекциям, этот подход становится выгодным, когда ресурс читают сильно чаще чем модифицируют.
Интерфейс реализуется классом ReentrantReadWriteLock, который во многом похож на обычный ReentrantLock.
Java Guru🤓 #java
👍6🔥4❤2
🗓 17 июля в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Java-разработчик».
Ошибки в кодировках ломают приложения, превращая текст в «абракадабру». Понимание таблиц кодировок — must-have навык для работы с данными, файлами и международными проектами.
О чём поговорим:
Кому будет интересно:
Начинающим Java-разработчикам и тестировщикам, сталкивающимся с международными данными и проблемами кодировки.
🔗 Ссылка на регистрацию: https://vk.cc/cNHZFM
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤2🔥2
Когда используется StampedLock?
StampedLock – примитив синхронизации, добавленный в Java с версии 8. Общий принцип его работы точно такой же, как у ReadWriteLock: захват неэксклюзивной блокировки (на чтение), и эксклюзивной (на запись). Но есть у этих классов ряд различий в деталях.
Во-первых, если блокировка ReadWriteLock возвращает объекты типа Lock, то StampedLock возвращает числа типа long, которые и называется «штампами». Штамп служит идентификатором лока, он передается параметром в методы по работе с ранее захваченной блокировкой чтения или записи. Специальный штамп 0 означает неудавшийся захват.
StampedLock в отличие от ReentrantReadWriteLock – не реентрант. Это накладывает бóльшую ответственность на программиста: можно устроить дедлок на одном потоке.
В StampedLock расширена функциональность. Новые методы с префиксом try* не висят в ожидании. Методы tryOptimistic* реализуют оптимистичную блокировку. Методы tryConvert* дают возможность изменять «уровень» заблокированности: можно попытаться превратить readLock во writeLock, и наоборот.
Не смотря на похожесть, StampedLock не наследуется от ReadWriteLock. Но для совместимости в нём предусмотрены методы-адаптеры asReadWriteLock, asReadLock и asWriteLock.
Итого, блокировка на штампах решает те же задачи, что ReadWriteLock, но дает больше возможностей и лучшую производительность.
Java Guru🤓 #java
StampedLock – примитив синхронизации, добавленный в Java с версии 8. Общий принцип его работы точно такой же, как у ReadWriteLock: захват неэксклюзивной блокировки (на чтение), и эксклюзивной (на запись). Но есть у этих классов ряд различий в деталях.
Во-первых, если блокировка ReadWriteLock возвращает объекты типа Lock, то StampedLock возвращает числа типа long, которые и называется «штампами». Штамп служит идентификатором лока, он передается параметром в методы по работе с ранее захваченной блокировкой чтения или записи. Специальный штамп 0 означает неудавшийся захват.
StampedLock в отличие от ReentrantReadWriteLock – не реентрант. Это накладывает бóльшую ответственность на программиста: можно устроить дедлок на одном потоке.
В StampedLock расширена функциональность. Новые методы с префиксом try* не висят в ожидании. Методы tryOptimistic* реализуют оптимистичную блокировку. Методы tryConvert* дают возможность изменять «уровень» заблокированности: можно попытаться превратить readLock во writeLock, и наоборот.
Не смотря на похожесть, StampedLock не наследуется от ReadWriteLock. Но для совместимости в нём предусмотрены методы-адаптеры asReadWriteLock, asReadLock и asWriteLock.
Итого, блокировка на штампах решает те же задачи, что ReadWriteLock, но дает больше возможностей и лучшую производительность.
Java Guru🤓 #java
👍7🔥6❤4