Оптимистичные замки и пессимистичные замки, о которых нужно сказать

Java задняя часть
Оптимистичные замки и пессимистичные замки, о которых нужно сказать

Мало знаний, большой вызов! Эта статья участвует в "Необходимые знания для программистов«Творческая деятельность

Эта статья также участвует"Проект "Звезда раскопок"", чтобы выиграть творческие подарочные наборы и бросить вызов творческим поощрениям

концепция

Оптимистическая блокировка и пессимистическая блокировка — это широкое понятие, но на самом деле это разные взгляды на синхронизацию потоков. Существуют практические применения этой концепции как в Java, так и в базах данных.

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

悲观.PNG

Оптимистическая блокировка: оптимистическая блокировка предполагает, что ни один другой поток не будет изменять данные при использовании данных, поэтому он не будет добавлять блокировки, а только при обновлении данных, чтобы определить, обновляли ли данные ранее другие потоки (конкретный метод может использовать версию числовой механизм и алгоритм CAS). Если эти данные не были обновлены, текущий поток успешно записывает данные, измененные им самим. Если данные были обновлены другими потоками, в зависимости от реализации выполняются разные операции: повторите попытку или сообщите об исключении.

乐观.PNG

Механизм управления параллелизмом

Когда в приложении может быть параллелизм, необходимо обеспечить точность данных в случае параллелизма, чтобы гарантировать, что, когда текущий пользователь работает вместе с другими пользователями, полученные результаты будут такими же, как и те, которые были получены при он действует один. Это называется контролем параллелизма. Цель контроля параллелизма — гарантировать, что работа одного пользователя не будет необоснованно влиять на работу другого пользователя. То есть данные выглядят как грязные чтения, фантомные чтения и неповторяемые чтения.

Часто говорят, что управление параллелизмом обычно связано с системой управления базами данных (СУБД). Задача управления параллелизмом в СУБД состоит в том, чтобы при одновременном добавлении, удалении, изменении и запросе одних и тех же данных несколькими транзакциями изоляция, согласованность и единство базы данных транзакций не нарушались. Основными средствами достижения контроля параллелизма являются оптимистичный контроль параллелизма и пессимистичный контроль параллелизма.

Механизм реализации оптимистичной блокировки

метод номера версии

Контроль номера версии: как правило, поле версии номера версии данных добавляется в таблицу данных, чтобы указать, сколько раз данные были изменены. При изменении данных значение версии будет +1. Когда поток A хочет обновить данные, он также считывает значение версии при чтении данных.При отправке обновления он будет обновляться только в том случае, если только что прочитанное значение версии равно значению версии в текущей базе данных, в противном случае повторите попытку обновления. до тех пор, пока обновление не будет успешным.

Например: в таблице пользователей в базе данных есть поле версии, текущее значение равно 0, а поле текущего баланса счета (деньги) равно 100.

Поток A считывает его в этот момент (версия = 0) и вычитает 50 из баланса своего аккаунта (100-50). Во время работы потока A поток B также считывает эту информацию о пользователе (версия = 0) и вычитает 30 (100-30) из баланса своего аккаунта. Поток A завершает работу по модификации, добавляет единицу к номеру версии данных (версия = 1) и отправляет его в базу данных для обновления вместе с балансом после вычета учетной записи (деньги = 50). больше, чем текущая версия записи базы данных, данные обновляются. , версия записи базы данных обновляется до 1. Поток B завершает операцию, а также добавляет номер версии на единицу (version=1) и пытается отправить данные в базу данных (money=70), но в это время при сравнении версий записей в базе данных обнаруживается, что номер версии данных, отправленный потоком B, равен 1, а база данных. Текущая версия записи также равна 1, что не удовлетворяет оптимистичной стратегии блокировки: «представленная версия должна быть больше, чем текущая версия записи для выполнения обновления». ". Таким образом, представление потока B отклонено. Таким образом, исключается возможность того, что оператор B перезапишет результат операции потока A результатом модификации старых данных на основе версии=0.

CAS-алгоритм

CAS означает сравнение и обмен, что является хорошо известным алгоритмом без блокировки. То есть для достижения переменной синхронизации между несколькими потоками без использования блокировок, то есть для достижения переменной синхронизации без блокировки потоков, поэтому ее еще называют неблокирующей синхронизацией (Non-blocking Synchronization)

В CAS задействованы три элемента:

Значение памяти V, которое необходимо прочитать и записать

Значение A для сравнения

Новое значение B, которое нужно записать

Измените значение памяти V на B тогда и только тогда, когда ожидаемое значение A и значение памяти V совпадают, в противном случае ничего не делайте.

Поддержка Java для CAS: недавно добавленная Java.util.concurrent (j.u.c) в JDK1.5 основано на CAS. Для алгоритма синхронизированного блокировки CAS является реализацией алгоритма неблокирования. Так что J.U.C имеет большое улучшение производительности. Для получения более подробной информации, пожалуйста, обратитесь к предыдущей статье:Я слышал, что вы хотите увидеть принцип CAS

Механизм реализации пессимистической блокировки

ReentrantLock

Блокировка с повторным входом — это тип пессимистической блокировки. Индикатор состояния синхронизации: отображает состояние занятости ресурсов блокировки для внешнего мира. Очередь синхронизации: хранит потоки, которым не удалось получить блокировки. Очередь ожидания: используется для пробуждения с несколькими условиями. Узел узла: каждый узел очереди, тело инкапсуляции потока. cas изменяет флаг состояния синхронизации, если ему не удается получить блокировку, он присоединяется к очереди синхронизации для блокировки и пробуждает поток первого узла очереди синхронизации при освобождении блокировки. Процесс блокировки: вызовите функцию tryAcquire(), чтобы изменить состояние идентификации, верните значение true для успешного выполнения и присоединитесь к очереди для ожидания в случае сбоя. После присоединения к очереди, чтобы определить, находится ли узел в состоянии сигнала, он напрямую заблокирует и приостановит текущий поток. Если это не так, оценивается, находится ли он в состоянии отмены, и если это так, он проходит вперед и удаляет все узлы состояния отмены в очереди. Если узел равен 0 или состояние распространения изменено на состояние сигнала. После пробуждения блока, если он является головным, он получит блокировку, вернет true в случае успеха и продолжит блокировку в случае сбоя. Процесс разблокировки: вызовите функцию tryRelease(), чтобы снять блокировку и изменить состояние идентификации, вернуть true в случае успеха и вернуть false в случае неудачи. После успешного снятия блокировки разбудите последующие заблокированные узлы потока в очереди синхронизации, и пробужденный узел автоматически заменит текущий узел в качестве головного узла. Для получения более подробной информации, пожалуйста, обратитесь к предыдущей статье:Поговорим о реентерабельной блокировке ReentrantLock

synchronized

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

public class Widget {
    public synchronized void doSomething() {
        System.out.println("方法1执行...");
        doOthers();
    }

    public synchronized void doOthers() {
        System.out.println("方法2执行...");
    }
}

Два метода в примере изменены встроенной синхронизацией блокировки, а метод doOthers() вызывается в методе doSomething(). Поскольку встроенная блокировка является реентерабельной, тот же поток может напрямую получить блокировку текущего объекта при вызове doOthers() и ввести doOthers() для операции. Если это блокировка без повторного входа, перед вызовом doOthers() текущий поток должен снять блокировку текущего объекта, полученную при выполнении doSomething(). Фактически, блокировка объекта уже удерживается текущим потоком и не может быть снята. выпущенный. Так что в это время будет тупик.

Проблема пессимистичной блокировки

ReentrantLock:

Необходимо ввести соответствующий класс;

Чтобы снять блокировку в модуле finally;

synchronized можно поместить в определение метода, а reentrantlock — только в блок;

синхронизировано:

Высвобождается несколько блокировок, и блокировки снимаются только тогда, когда нормальное выполнение программы завершается и выдается исключение;

Попытка получить блокировку не может установить тайм-аут;

Поток, пытающийся получить блокировку, не может быть прерван;

Невозможно узнать, была ли блокировка успешно получена;

как выбрать

Пессимистические блокировки подходят для сценариев с большим количеством операций записи. Первая блокировка может гарантировать правильность данных во время операций записи. Оптимистическая блокировка подходит для сценариев с большим количеством операций чтения, а функция разблокировки может значительно повысить производительность операций чтения.

//悲观锁
public synchronized void testMethod() {
	// 操作同步资源
}
// ReentrantLock
private ReentrantLock lock = new ReentrantLock(); // 需要保证多个线程使用的是同一个锁
public void modifyPublicResources() {
	lock.lock();
	// 操作同步资源
	lock.unlock();
}

//乐观锁
private AtomicInteger atomicInteger = new AtomicInteger();  // 需要保证多个线程使用的是同一个AtomicInteger
atomicInteger.incrementAndGet(); //执行自增1

На примере видно, что пессимистичные блокировки в основном управляют ресурсами синхронизации после явной блокировки, а оптимистичные блокировки непосредственно управляют ресурсами синхронизации.

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

Оптимистическая блокировка на самом деле не блокирует, и она эффективна.一旦锁的粒度掌握不好,更新失败的概率就会比较高,容易发生业务失败。

Пессимистичные блокировки основаны на блокировках базы данных и неэффективны. Вероятность сбоя обновления относительно низка.

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