StampedLock в Java | OTUS
🔥 BLACK FRIDAY!
Максимальная скидка -25% на всё. Успейте начать обучение по самой выгодной цене.
Выбрать курс

Курсы

Программирование
iOS Developer. Basic
-25%
Python Developer. Professional
-25%
Разработчик на Spring Framework
-25%
Golang Developer. Professional
-25%
Python Developer. Basic
-25%
iOS Developer. Professional
-25%
Highload Architect
-25%
JavaScript Developer. Basic
-25%
Kotlin Backend Developer
-25%
JavaScript Developer. Professional
-25%
Android Developer. Basic
-25%
Unity Game Developer. Basic
-25%
Разработчик C#
-25%
Программист С Web-разработчик на Python Алгоритмы и структуры данных Framework Laravel PostgreSQL Reverse-Engineering. Professional CI/CD Vue.js разработчик VOIP инженер Программист 1С Flutter Mobile Developer Супер - интенсив по Kubernetes Symfony Framework Advanced Fullstack JavaScript developer Супер-интенсив "Azure для разработчиков"
Инфраструктура
Мониторинг и логирование: Zabbix, Prometheus, ELK
-25%
DevOps практики и инструменты
-25%
Архитектор сетей
-25%
Инфраструктурная платформа на основе Kubernetes
-25%
Супер-интенсив «IaC Ansible»
-16%
Разработчик программных роботов (RPA) на базе UiPath и PIX
-25%
Супер-интенсив "SQL для анализа данных"
-16%
Базы данных Сетевой инженер AWS для разработчиков Cloud Solution Architecture Разработчик голосовых ассистентов и чат-ботов Внедрение и работа в DevSecOps Администратор Linux. Виртуализация и кластеризация Нереляционные базы данных Супер-практикум по использованию и настройке GIT IoT-разработчик Супер-интенсив «ELK»
Специализации Курсы в разработке Подготовительные курсы
+7 499 938-92-02

StampedLock в Java

Java_Deep_16.12-5020-8569f2.png

В Java 8 в пакете java.util.concurrent.locks появился интересный класс – StampedLock. Этот класс в ряде случаев приносит исключительную пользу, однако не все даже опытные программисты про него знают. Сегодня мы немного подправим эту досадную ситуацию.

Из названия очевидно, что класс StampedLock реализует механизм блокировок и является функциональным аналогом хорошо известным механизмам synchronized и ReentrantLock.

Оптимистичная блокировка

У StampedLock есть ряд интересных особенностей, сегодня мы рассмотрим одну из них – «оптимистичная блокировка». «Оптимистичная блокировка» – широко известный принцип в организации многопользовательского доступа к базам данных. Принцип работы очень простой – читаем данные, надеясь, что их никто не успел изменить. Если всё же кто-то поменял, то читаем ещё раз или выставляем блокировку (если уровень оптимизма уменьшился и читаем ещё раз.

Рассмотрим пример

Есть общая переменная. Один поток эту переменную меняет, два другие читают. Причём поток-писатель делает своё дело долго, но относительно редко. А читатели читают часто, но быстро. Как бы мы реализовали эту схему «традиционными средствами»? Писатель и читатели блокировали бы общую переменную для выполнения своих действий. При этом они мешали бы друг другу и общая производительность системы снижалась бы. Мы могли бы использовать раздельные блокировки – на чтение и на запись. Стало бы лучше, но не сильно. Потоки всё равно «мешали» бы друг другу. Т. к. писатель один и работает редко, мы можем использовать «оптимистичную блокировку» в надежде на то, что писатель в большинстве случаев не успевает изменить данные.

Как это выглядит в коде:

    private void counterWriter() {
        try {
            while (!Thread.currentThread().isInterrupted()) {
                long stamp = sl.writeLock(); // выставляем блокировку на запись
                try {
                    long tmp = counter;
                    System.out.println("start counter modification:" + tmp);
                    Thread.sleep(10_000);
                    tmp++;
                    counter = tmp; // изменяем общую переменную.
                    System.out.println("end counter modification:" + tmp);
                } finally {
                    sl.unlockWrite(stamp); //снимаем блокировку на запись
                }
                Thread.sleep(30_000);
            }
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
        }
    }

Как выглядит читатель:

    private void counterReader(int id) {
        try {
            while (!Thread.currentThread().isInterrupted()) {
                long stamp = sl.tryOptimisticRead(); // берем метку состояния
                long tmp = counter; // читаем значение общей переменной
                if (!sl.validate(stamp)) { // проверяем метку состояния, 
                    System.out.println("    id:" + id + " protected value has been changed");
                    stamp = sl.readLock(); // если состояние изменилось, ставим блокировку
                    System.out.println("    id:" + id + " new readLock");
                    try {
                        tmp = counter; // читаем данные под блокировкой
                    } finally {
                        sl.unlockRead(stamp); // снимаем блокировку
                    }
                }
                System.out.println("    id:" + id + " current value:" + tmp);
                Thread.sleep(1_000);
            }
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
        }
    }

Логика работы основана на метке – значении, которое отражает состояние данных, которые мы защищаем критической секцией.

Если между моментом «фиксации состояния» (long stamp = sl.tryOptimisticRead();) и проверкой (sl.validate(stamp))) метка изменилась, значит кто-то изменил общее состояние, и данные надо перечитать.

Почему этот подход более эффективен, чем например synchronized? Т. к. данные меняются редко, то нет необходимости на каждое чтение выставлять блокировку, которая является весьма ресурсозатратной операцией.

Полный пример находится по ссылке.

Не пропустите новые полезные статьи!

Спасибо за подписку!

Мы отправили вам письмо для подтверждения вашего email.
С уважением, OTUS!

Автор
0 комментариев
Для комментирования необходимо авторизоваться
🎁 Максимальная скидка!
Черная пятница уже в OTUS! Скидка -25% на всё!