← Назад к вопросам

Какие знаешь альтернативы у synchronized?

1.8 Middle🔥 141 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Альтернативы synchronized в Java

Вопрос про синхронизацию показывает хорошее понимание concurrency. Synchronized это базовый инструмент, но есть много альтернатив с разными характеристиками.

1. ReentrantLock из java.util.concurrent

Это наиболее гибкая альтернатива synchronized с множеством преимуществ:

public class LockExample {
    private final ReentrantLock lock = new ReentrantLock();
    private int counter = 0;
    
    // Базовое использование
    public void increment() {
        lock.lock();
        try {
            counter++;
        } finally {
            lock.unlock();
        }
    }
    
    // Тайм-ауты
    public boolean incrementWithTimeout() throws InterruptedException {
        if (lock.tryLock(1, TimeUnit.SECONDS)) {
            try {
                counter++;
                return true;
            } finally {
                lock.unlock();
            }
        }
        return false;
    }
    
    // Interruptible версия
    public void incrementInterruptible() throws InterruptedException {
        lock.lockInterruptibly();
        try {
            counter++;
        } finally {
            lock.unlock();
        }
    }
}

Преимущества ReentrantLock:

  • Попытка захвата с тайм-аутом: tryLock(timeout, unit)
  • Прерывание: lockInterruptibly()
  • Fair mode: справедливое распределение доступа между потоками
  • Condition variables: для более сложной синхронизации
  • Встроенный счётчик: поддерживает reentrancy

2. ReadWriteLock для асимметричного доступа

Когда у вас много читателей и мало писателей:

public class CachedUserService {
    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    private final Map<Long, User> cache = new HashMap<>();
    
    // Множество потоков могут читать одновременно
    public User getUserFromCache(Long id) {
        lock.readLock().lock();
        try {
            return cache.get(id);
        } finally {
            lock.readLock().unlock();
        }
    }
    
    // Только один поток может писать
    public void updateUserInCache(Long id, User user) {
        lock.writeLock().lock();
        try {
            cache.put(id, user);
        } finally {
            lock.writeLock().unlock();
        }
    }
    
    // Downgrade: запись -> чтение
    public void upgradeAndRead(Long id) {
        lock.readLock().lock();
        try {
            if (!cache.containsKey(id)) {
                lock.readLock().unlock();
                lock.writeLock().lock();
                try {
                    // Теперь у нас есть write lock
                    cache.put(id, new User());
                    lock.readLock().lock();
                } finally {
                    lock.writeLock().unlock();
                }
            }
        } finally {
            lock.readLock().unlock();
        }
    }
}

3. StampedLock (Java 8+) для экстремальной производительности

Получает stamp при захвате, может проверить валидность без блокировки:

public class OptimizedCache {
    private final StampedLock lock = new StampedLock();
    private volatile long version = 0;
    private volatile String cachedData = "";
    
    // Оптимистичное чтение - без блокировки!
    public String getDataOptimistic() {
        long stamp = lock.tryOptimisticRead();
        String data = cachedData;
        long currentVersion = version;
        
        if (!lock.validate(stamp)) {
            // Данные изменились, нужна обычная блокировка
            stamp = lock.readLock();
            try {
                data = cachedData;
            } finally {
                lock.unlockRead(stamp);
            }
        }
        return data;
    }
    
    public void updateData(String newData) {
        long stamp = lock.writeLock();
        try {
            version++;
            cachedData = newData;
        } finally {
            lock.unlockWrite(stamp);
        }
    }
}

StampedLock идеален для высоконагруженных систем с множеством читающих потоков.

4. AtomicInteger, AtomicLong, AtomicReference для простых операций

Для простых случаев, когда не нужна сложная синхронизация:

public class AtomicCounterExample {
    private final AtomicInteger counter = new AtomicInteger(0);
    private final AtomicLong timestamp = new AtomicLong(0);
    private final AtomicReference<User> currentUser = new AtomicReference<>();
    
    public void incrementCounter() {
        counter.incrementAndGet();
    }
    
    public int getAndIncrementCounter() {
        return counter.getAndIncrement();
    }
    
    public void compareAndSetUser(User oldUser, User newUser) {
        currentUser.compareAndSet(oldUser, newUser);
    }
    
    // Операции с updateAndGet
    public int updateCounterWithCallback() {
        return counter.updateAndGet(current -> current * 2 + 1);
    }
}

Основано на CAS (Compare-And-Swap) операциях на уровне CPU. Очень быстро, не требует блокировок.

5. Semaphore для контроля количества потоков

Когда нужно ограничить количество потоков, обращающихся к ресурсу:

public class ConnectionPoolService {
    private final Semaphore connectionSemaphore = new Semaphore(10);
    private final Queue<Connection> connectionPool = new LinkedList<>();
    
    public Connection getConnection() throws InterruptedException {
        connectionSemaphore.acquire(); // Блокирует если нет свободных
        
        synchronized(connectionPool) {
            return connectionPool.poll();
        }
    }
    
    public void releaseConnection(Connection conn) {
        synchronized(connectionPool) {
            connectionPool.offer(conn);
        }
        connectionSemaphore.release(); // Разрешает следующему потоку
    }
}

6. CyclicBarrier и CountDownLatch для координации

Для синхронизации между несколькими потоками:

public class ParallelProcessing {
    // CountDownLatch: один поток ждёт завершения других
    public void processWithLatch(List<String> items) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(items.size());
        
        for (String item : items) {
            new Thread(() -> {
                try {
                    processItem(item);
                } finally {
                    latch.countDown();
                }
            }).start();
        }
        
        latch.await(); // Ждём завершения всех
    }
    
    // CyclicBarrier: несколько потоков ждут друг друга
    public void parallelPhases(int numWorkers) throws InterruptedException {
        CyclicBarrier barrier = new CyclicBarrier(numWorkers, () -> {
            System.out.println("Phase complete, starting next");
        });
        
        for (int i = 0; i < numWorkers; i++) {
            new Thread(() -> {
                try {
                    barrier.await(); // Ждём остальных
                    System.out.println("All ready, proceeding");
                } catch (InterruptedException | BrokenBarrierException e) {
                    Thread.currentThread().interrupt();
                }
            }).start();
        }
    }
}

7. ConcurrentHashMap и другие потокобезопасные коллекции

Под капотом используют segmented locking вместо одного большого замка:

public class ConcurrentCollectionsExample {
    private final ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
    private final CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
    private final ConcurrentLinkedQueue<Task> queue = new ConcurrentLinkedQueue<>();
    
    public void putIfAbsent(String key, Integer value) {
        // Атомарная операция - меньше блокировок чем synchronized
        map.putIfAbsent(key, value);
    }
    
    public void addToList(String item) {
        // Хорошо для частого чтения, редкого изменения
        list.add(item);
    }
    
    public void addTask(Task task) {
        // Lock-free, на основе CAS
        queue.offer(task);
    }
}

Сравнение характеристик

ПодходКонтестПроизводительностьГибкостьСложность
synchronizedВстроен в языкСредняяНизкаяНизкая
ReentrantLockj.u.c.locksВысокаяВысокаяСредняя
ReadWriteLockj.u.c.locksОчень высокая (read-heavy)СредняяСредняя
StampedLockj.u.c.locksЭкстремальнаяВысокаяВысокая
Atomic*j.u.c.atomicМаксимальнаяНизкаяНизкая
ConcurrentHashMapj.u.cОчень высокаяНизкаяНизкая

Рекомендации по выбору

Используй synchronized когда:

  • Простая синхронизация всего объекта
  • Критические секции очень короткие
  • Не нужны тайм-ауты или прерывания

Используй ReentrantLock когда:

  • Нужны тайм-ауты или попытка захвата
  • Нужна справедливость (fair mode)
  • Нужны Condition variables

Используй ReadWriteLock когда:

  • Много читателей, мало писателей
  • Операции чтения дорогие

Используй StampedLock когда:

  • Экстремальная производительность критична
  • Основной рабочий нагрузок чтение
  • Код может быть сложнее

Используй Atomic когда:

  • Синхронизация одной переменной
  • Максимальная производительность

Используй ConcurrentCollections когда:

  • Используешь Maps, Lists, Queues
  • Не нужна глобальная синхронизация

Выбор правильного инструмента синхронизации критичен для production систем, потому что это влияет как на корректность, так и на производительность.