Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
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 — если критична производительность для читателей
Сравнение блокировок
| Аспект | Shared | Exclusive | Optimistic |
|---|---|---|---|
| Множественные читатели | Да | Нет | Да |
| Конфликты | Низкие | Высокие | Очень низкие |
| Производительность | Хорошая | Плохая при конкуренции | Отличная для читателей |
| Сложность | Средняя | Простая | Высокая |
Shared Lock — это критический механизм для обеспечения параллельного доступа к данным при минимизации конфликтов в многопоточных приложениях и системах управления базами данных.