Когда дело доходит до блокировок, каждый может подумать о ключевом слове synchronized.Использование его действительно может решить все проблемы параллелизма, но для тех, у кого более высокие требования к пропускной способности системы, вот несколько советов, которые помогут вам уменьшить гранулярность блокировок и улучшить возможности параллелизма системы.
Совет для начинающих — оптимистичная блокировка
Оптимистическая блокировка подходит для таких сценариев: чтение не будет конфликтовать, запись будет конфликтовать. Частота одновременных операций чтения намного выше, чем частота операций записи.
Возьмем в качестве примера следующий код, реализацию пессимистической блокировки:
Реализация оптимистической блокировки:
Промежуточные навыки — String.intern()
Оптимистическая блокировка не может решить проблему большого количества конфликтов записи, но во многих сценариях блокировка на самом деле только для определенного пользователя или заказа. Например, пользователь должен создать сеанс перед выполнением последующих операций. Однако из-за сетевых причин запрос на создание сеанса пользователя и последующие запросы поступают практически одновременно, и параллельные потоки могут обрабатывать последующие запросы первыми. В общем, необходимо заблокировать пользователя sessionMap, например оптимистическую блокировку выше. В этом случае блокировка может быть ограничена самим пользователем, т. е. исходным
lock.lock();
int num=storage.get(key);
storage.set(key,num+1);
lock.unlock();
изменить на:
lock.lock(key);
int num=storage.get(key);
storage.set(key,num+1);
lock.unlock(key);
Это сравнение похоже на концепции блокировок таблиц и строк базы данных.Очевидно, что возможности параллелизма блокировок строк намного выше, чем у блокировок таблиц.
Использование String.inter() является конкретной реализацией этой идеи. Класс String поддерживает пул строк. При вызове внутреннего метода, если пул уже содержит строку, равную этому объекту String (как определено методом equals(Object)), возвращается строка из пула. Видно, что когда String одинакова, String.intern() всегда возвращает один и тот же объект, тем самым реализуя блокировку одного и того же пользователя. Поскольку степень детализации блокировки ограничена конкретными пользователями, система получает максимальную степень параллелизма.
Копировать при записи карты?
Теперь, когда мы поговорили о «концепции блокировки строк в базе данных», мы должны упомянуть MVCC, класс CopyOnWrite в Java реализует MVCC. Копирование при записи является таким механизмом. Когда мы читаем общие данные, мы читаем их напрямую без синхронизации. Когда мы изменяем данные, мы делаем копию текущих данных, а затем модифицируем их на этой копии.После завершения заменяем исходные данные измененной копией. Этот метод называется копированием при записи.
Однако JDK не предоставляет CopyOnWriteMap, почему? Ниже есть хороший ответ, то есть ConcurrentHashMap уже есть, зачем вам CopyOnWriteMap?
Дополнительные советы — класс ConcurrentHashMap
Недостатком String.inter() является то, что класс String поддерживает пул строк, который размещается в перманентной области JVM.Если количество пользователей слишком велико, String, помещенный в пул строк, будет неуправляемым, что может привести к Ошибки OOM или чрезмерный полный сборщик мусора. Как контролировать количество блокировок и одновременно уменьшить степень детализации блокировок? Использовать Java ConcurrentHashMap напрямую? Или вы хотите добавить свой собственный более тонкий контроль? Затем вы можете узнать из метода ConcurrentHashMap, как разделить объекты, которые необходимо заблокировать, на несколько сегментов и добавить блокировку для каждого сегмента.Псевдокод выглядит следующим образом: