← Назад к вопросам
Какие блокировки лучше по производительности
1.7 Middle🔥 191 комментариев
#Многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Какие блокировки лучше по производительности
В Java существует множество механизмов синхронизации. Выбор правильного — критичен для производительности многопоточного приложения.
1. synchronized — встроенная блокировка
// Самая простая, но не самая быстрая
public class SynchronizedExample {
private int counter = 0;
// Метод-блокировка: использует монитор объекта
public synchronized void increment() {
counter++;
}
// Блоки-блокировка
public void incrementBlock() {
synchronized (this) {
counter++;
}
}
}
// Производительность: O(high) при high contention
// Плюсы:
// - Простой синтаксис
// - Автоматическое освобождение при исключении
// - Оптимизирован JVM (biased locking, lock coarsening)
// Минусы:
// - Блокирует весь объект
// - Грубая гранулярность
2. ReentrantLock — явная блокировка
import java.util.concurrent.locks.ReentrantLock;
// Более гибкая и быстрая чем synchronized
public class ReentrantLockExample {
private final ReentrantLock lock = new ReentrantLock();
private int counter = 0;
public void increment() {
lock.lock();
try {
counter++;
} finally {
lock.unlock();
}
}
// Попытка захватить блокировку с timeout
public boolean tryIncrement(long timeout) throws InterruptedException {
if (lock.tryLock(timeout, TimeUnit.MILLISECONDS)) {
try {
counter++;
return true;
} finally {
lock.unlock();
}
}
return false;
}
}
// Производительность: O(medium) при high contention
// Плюсы:
// - Быстрее чем synchronized при конкуренции
// - Поддерживает fairness
// - tryLock() для non-blocking
// Минусы:
// - Нужно вручную освобождать
// - Более сложный синтаксис
3. ReadWriteLock — блокировка читает/записи
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
// Идеально когда много читателей, мало писателей
public class ReadWriteLockExample {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private Map<String, String> cache = new HashMap<>();
// Несколько потоков могут одновременно читать
public String get(String key) {
lock.readLock().lock();
try {
return cache.get(key);
} finally {
lock.readLock().unlock();
}
}
// Только один поток может писать
public void put(String key, String value) {
lock.writeLock().lock();
try {
cache.put(key, value);
} finally {
lock.writeLock().unlock();
}
}
}
// Производительность: O(low) для reads, O(medium) для writes
// Плюсы:
// - Параллельное чтение
// - Отлично для кешей
// Минусы:
// - Медленнее чем ReentrantLock если много записей
4. StampedLock — оптимистичная блокировка (Java 8+)
import java.util.concurrent.locks.StampedLock;
// Самая быстрая для high read/low write сценариев
public class StampedLockExample {
private final StampedLock lock = new StampedLock();
private double x, y;
// Оптимистичное чтение (очень быстро)
public double distanceFromOrigin() {
long stamp = lock.tryOptimisticRead(); // Не блокируем!
double currentX = x;
double currentY = y;
// Проверяем, не изменились ли данные
if (!lock.validate(stamp)) {
// Данные изменились, повторяем с блокировкой
stamp = lock.readLock();
try {
currentX = x;
currentY = y;
} finally {
lock.unlockRead(stamp);
}
}
return Math.hypot(currentX, currentY);
}
// Эксклюзивная запись
public void move(double newX, double newY) {
long stamp = lock.writeLock();
try {
x = newX;
y = newY;
} finally {
lock.unlockWrite(stamp);
}
}
}
// Производительность: O(very low) для optimistic reads
// Плюсы:
// - Оптимистичное чтение без блокировки
// - Быстрее ReadWriteLock при много reads
// Минусы:
// - Более сложный синтаксис
// - Нужно иметь fallback
5. Atomic классы — lock-free синхронизация
import java.util.concurrent.atomic.*;
// Используют CAS (Compare-And-Swap) вместо блокировок
public class AtomicExample {
// Для примитивов
private AtomicInteger counter = new AtomicInteger(0);
private AtomicLong timestamp = new AtomicLong(0);
private AtomicBoolean flag = new AtomicBoolean(false);
// Для объектов
private AtomicReference<String> name = new AtomicReference<>("John");
public void increment() {
counter.incrementAndGet(); // Атомарная операция
}
public int getAndReset() {
return counter.getAndSet(0); // Свопируем значение
}
}
// Производительность: O(very low) при low contention
// Плюсы:
// - Нет блокировок, нет deadlock'ов
// - Очень быстро при низкой конкуренции
// Минусы:
// - Только для простых операций
// - При высокой конкуренции медленнее блокировок
6. ConcurrentHashMap — специализированная структура
import java.util.concurrent.ConcurrentHashMap;
// Использует bucket-level лocking вместо таблица-level
public class ConcurrentHashMapExample {
private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
public void update(String key) {
// Только bucket, содержащий этот ключ, блокируется
map.put(key, 1);
// Атомарная операция
map.computeIfPresent(key, (k, v) -> v + 1);
}
}
// Производительность: O(low) для most operations
// Плюсы:
// - Лучше чем Collections.synchronizedMap()
// - Parallel streams support
// Минусы:
// - Не поддерживает итерацию без снимков
Сравнение производительности
Высокая конкуренция (многие потоки пишут):
1. Atomic* (очень быстро для простых операций)
2. ReentrantLock (быстро)
3. synchronized (JVM оптимизирует, может быть быстро)
4. ReadWriteLock (медленнее, так как много конкуренции)
5. StampedLock optimistic (медленнее, часто переходит в read lock)
Низкая конкуренция (мало потоков пишут, много читают):
1. StampedLock optimistic (очень быстро)
2. ReadWriteLock (быстро, много параллельного чтения)
3. Atomic* (быстро)
4. ReentrantLock (в порядке)
5. synchronized (в порядке, но меньше контроля)
Практический выбор
// Для счетчиков и простых операций
private AtomicInteger counter = new AtomicInteger(0);
// Для коллекций
private Map<String, String> map = new ConcurrentHashMap<>();
List<String> list = Collections.synchronizedList(new ArrayList<>());
// Для critical section с несколькими операциями
private final Object lock = new Object();
synchronized (lock) {
// несколько операций
}
// Для read-heavy сценариев
private ReadWriteLock lock = new ReentrantReadWriteLock();
// Для очень read-heavy (Java 8+)
private StampedLock lock = new StampedLock();
Best Practices
- Используй Atomic для простых операций* — самые быстрые
- ConcurrentHashMap вместо synchronized Map — намного быстрее
- ReadWriteLock если много readers — параллельное чтение
- StampedLock если много readers и Java 8+ — оптимистичное чтение
- synchronized для простых critical sections — JVM оптимизирует хорошо
- Избегай вложенных блокировок — риск deadlock'ов
- Мониторь contention — используй profiler для анализа
Выбор правильной синхронизации может дать 10x улучшение производительности!