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

Что такое Shared Lock?

1.3 Junior🔥 201 комментариев
#Soft Skills и карьера

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

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

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

Shared Lock

Shared Lock (разделяемая блокировка) — это механизм синхронизации в базах данных и многопоточном программировании, который позволяет множеству потоков одновременно читать ресурс, но запрещает модификацию. Это контрастирует с Exclusive Lock (исключительной блокировкой), которая предоставляет доступ только одному потоку.

Типы блокировок в БД

Shared Lock (Блокировка для чтения)

Позволяет нескольким пользователям одновременно читать данные:

public class SharedLockExample {
    // В PostgreSQL
    // SELECT ... FOR SHARE
    // Несколько читателей могут держать блокировку одновременно
    
    public void readDataWithSharedLock() {
        String query = "SELECT * FROM products WHERE id = ? FOR SHARE";
        
        // Несколько потоков могут выполнять этот запрос одновременно
        // Но если кто-то захочет изменить data, должен будет ждать
    }
}

Exclusive Lock (Исключительная блокировка)

Только один поток может держать такую блокировку:

public class ExclusiveLockExample {
    // В PostgreSQL
    // SELECT ... FOR UPDATE
    // Только один читатель может держать блокировку
    
    public void updateDataWithExclusiveLock() {
        String query = "SELECT * FROM products WHERE id = ? FOR UPDATE";
        
        // Только один поток может выполнять этот запрос
        // Остальные потоки должны ждать
    }
}

ReadWriteLock в Java

Java предоставляет ReadWriteLock интерфейс для управления блокировками при чтении и записи:

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockExample {
    
    private ReadWriteLock lock = new ReentrantReadWriteLock();
    private int count = 0;
    
    // Многопоточное чтение
    public int readCount() {
        lock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " reading");
            return count;
        } finally {
            lock.readLock().unlock();
        }
    }
    
    // Исключительная запись
    public void incrementCount() {
        lock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " writing");
            count++;
        } finally {
            lock.writeLock().unlock();
        }
    }
    
    public static void main(String[] args) {
        ReadWriteLockExample example = new ReadWriteLockExample();
        
        // Несколько читателей могут работать одновременно
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                for (int j = 0; j < 5; j++) {
                    System.out.println("Value: " + example.readCount());
                }
            }).start();
        }
        
        // Писатель должен ждать или вытеснить читателей
        new Thread(() -> {
            for (int j = 0; j < 5; j++) {
                example.incrementCount();
            }
        }).start();
    }
}

Shared Lock в БД — примеры

// PostgreSQL — разделяемая блокировка при чтении
SELECT * FROM users WHERE id = 1 FOR SHARE;

// MySQL
SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE;

// Oracle
SELECT * FROM users WHERE id = 1 FOR UPDATE;

// Разница:
// FOR SHARE — другие транзакции могут читать и тоже получить FOR SHARE
// FOR UPDATE — другие транзакции должны ждать

Практический пример с Shared Lock

import java.sql.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SharedLockDatabase {
    
    private String connectionUrl = "jdbc:postgresql://localhost/mydb";
    
    public void multipleReadersWithSharedLock() throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        
        // Несколько потоков читают с FOR SHARE
        for (int i = 0; i < 5; i++) {
            final int threadId = i;
            executor.submit(() -> {
                try (Connection conn = DriverManager.getConnection(connectionUrl)) {
                    conn.setAutoCommit(false);
                    
                    String query = "SELECT * FROM products WHERE id = 1 FOR SHARE";
                    try (Statement stmt = conn.createStatement()) {
                        ResultSet rs = stmt.executeQuery(query);
                        
                        System.out.println("Thread " + threadId + " reading");
                        Thread.sleep(2000); // Имитация долгого чтения
                        
                        while (rs.next()) {
                            System.out.println("Product: " + rs.getString("name"));
                        }
                    }
                    
                    conn.commit();
                    System.out.println("Thread " + threadId + " done");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        
        executor.shutdown();
        executor.awaitTermination(1, java.util.concurrent.TimeUnit.MINUTES);
    }
}

Совместимость блокировок

            Shared  Exclusive
Shared        OK       NO
Exclusive     NO       NO

// Shared Lock совместима с другими Shared Locks
// Exclusive Lock не совместима ни с чем

Deadlock со Shared Locks

Хотя Shared Locks безопаснее, они могут привести к deadlock:

public class SharedLockDeadlock {
    
    // Потенциальный deadlock сценарий
    // Thread 1: Получает Shared Lock на Table A
    // Thread 2: Получает Shared Lock на Table B
    // Thread 1: Ждёт Exclusive Lock на Table B
    // Thread 2: Ждёт Exclusive Lock на Table A
    // DEADLOCK!
    
    public void transactionA() {
        // SELECT * FROM users FOR SHARE
        // UPDATE accounts SET balance = ...
    }
    
    public void transactionB() {
        // SELECT * FROM accounts FOR SHARE
        // UPDATE users SET name = ...
    }
}

Lock Timeout

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.TimeUnit;

public class LockTimeout {
    
    private ReadWriteLock lock = new ReentrantReadWriteLock();
    
    public void readWithTimeout() throws InterruptedException {
        // Ждём получить read lock максимум 5 секунд
        if (lock.readLock().tryLock(5, TimeUnit.SECONDS)) {
            try {
                System.out.println("Got read lock");
            } finally {
                lock.readLock().unlock();
            }
        } else {
            System.out.println("Could not get read lock");
        }
    }
    
    public void writeWithTimeout() throws InterruptedException {
        // Ждём получить write lock максимум 5 секунд
        if (lock.writeLock().tryLock(5, TimeUnit.SECONDS)) {
            try {
                System.out.println("Got write lock");
            } finally {
                lock.writeLock().unlock();
            }
        } else {
            System.out.println("Could not get write lock");
        }
    }
}

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

Java 8 представила StampedLock как более быструю альтернативу:

import java.util.concurrent.locks.StampedLock;

public class StampedLockExample {
    
    private StampedLock lock = new StampedLock();
    private int value = 0;
    
    // Оптимистичное чтение (без блокировки!)
    public int readOptimistic() {
        long stamp = lock.tryOptimisticRead();
        
        int result = value;
        
        // Проверяем если данные не изменились во время чтения
        if (!lock.validate(stamp)) {
            // Fallback на обычный read lock
            stamp = lock.readLock();
            try {
                result = value;
            } finally {
                lock.unlockRead(stamp);
            }
        }
        
        return result;
    }
    
    public void write(int newValue) {
        long stamp = lock.writeLock();
        try {
            value = newValue;
        } finally {
            lock.unlockWrite(stamp);
        }
    }
}

Best Practices с Shared Locks

  • Используй Shared Lock для читаемых данных — даёт лучшую параллельность
  • Минимизируй время блокировки — держи lock только столько сколько нужно
  • Избегай lock upgrading — читай сначала, пиши потом
  • Используй timeout — избегай бесконечного ожидания
  • Мониторь deadlocks — логируй и отслеживай конфликты блокировок
  • Используй StampedLock — если критична производительность для читателей

Сравнение блокировок

АспектSharedExclusiveOptimistic
Множественные читателиДаНетДа
КонфликтыНизкиеВысокиеОчень низкие
ПроизводительностьХорошаяПлохая при конкуренцииОтличная для читателей
СложностьСредняяПростаяВысокая

Shared Lock — это критический механизм для обеспечения параллельного доступа к данным при минимизации конфликтов в многопоточных приложениях и системах управления базами данных.