Ответ
Пессимистическая блокировка предполагает, что конфликт при совместном доступе к ресурсу вероятен, поэтому поток заранее захватывает эксклюзивную блокировку.
1. На уровне языка (синхронизация)
Использование ключевого слова synchronized или класса ReentrantLock из java.util.concurrent.locks.
Пример с ReentrantLock:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class PessimisticCounter {
private int value = 0;
private final Lock lock = new ReentrantLock(); // Пессимистическая блокировка
public void safeIncrement() {
lock.lock(); // Захват блокировки. Поток будет ждать, если блокировка у другого.
try {
value++; // Критическая секция защищена
} finally {
lock.unlock(); // ОСВОБОЖДЕНИЕ в finally — ОБЯЗАТЕЛЬНО!
}
}
public int getValue() {
lock.lock();
try {
return value;
} finally {
lock.unlock();
}
}
}
2. На уровне базы данных (JDBC)
Использование SELECT ... FOR UPDATE для блокировки строк.
Пример с JDBC и транзакцией:
Connection conn = dataSource.getConnection();
try {
conn.setAutoCommit(false); // Начало транзакции
// 1. ПЕССИМИСТИЧЕСКАЯ БЛОКИРОВКА строки
String selectSQL = "SELECT balance FROM accounts WHERE id = ? FOR UPDATE";
PreparedStatement selectStmt = conn.prepareStatement(selectSQL);
selectStmt.setInt(1, accountId);
ResultSet rs = selectStmt.executeQuery();
// Строка с accountId теперь заблокирована для других транзакций
// 2. Чтение и модификация данных
int currentBalance = 0;
if (rs.next()) {
currentBalance = rs.getInt("balance");
}
rs.close();
selectStmt.close();
int newBalance = currentBalance - 100;
// 3. Обновление заблокированной строки
String updateSQL = "UPDATE accounts SET balance = ? WHERE id = ?";
PreparedStatement updateStmt = conn.prepareStatement(updateSQL);
updateStmt.setInt(1, newBalance);
updateStmt.setInt(2, accountId);
updateStmt.executeUpdate();
updateStmt.close();
conn.commit(); // Фиксация транзакции и снятие блокировки
} catch (SQLException e) {
conn.rollback(); // Откат в случае ошибки
throw e;
} finally {
conn.setAutoCommit(true);
conn.close();
}
| Сравнение с оптимистической блокировкой: | Аспект | Пессимистическая | Оптимистическая |
|---|---|---|---|
| Философия | Конфликты часты, блокируем заранее. | Конфликты редки, проверяем при коммите. | |
| Механизм | synchronized, Lock, FOR UPDATE. |
Версионность (version column), CAS. |
|
| Производительность | Может быть ниже из- ожидания блокировок. | Выше при низкой конкуренции. | |
| Применение | Высокая конкуренция, критичные данные. | Чтение >> записи, низкая конкуренция. |
Ответ 18+ 🔞
А, слушай, пессимистическая блокировка — это когда ты такой заранее уверен, что все вокруг — мудаки, которые норовят тебе нагадить в общий ресурс. Ну, типа, конфликт — это не «если», а «когда». Поэтому подход простой, как три копейки: подошёл, схватил за горло этот ресурс, повесил на него здоровенный замок и кричишь «Моё! Пока я не отпущу — никому ни хуя!».
1. На уровне языка (синхронизация)
Тут всё просто: либо synchronized, либо этот серьёзный дядька — ReentrantLock из пакета java.util.concurrent.locks. Он как бугай с бейсбольной битой у входа в бар: «Проход закрыт, пока я не закончу свои дела».
Смотри, как это выглядит с ReentrantLock:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class PessimisticCounter {
private int value = 0;
private final Lock lock = new ReentrantLock(); // Вот он, наш пессимист-бугай
public void safeIncrement() {
lock.lock(); // Захватываем блокировку. Если занято — сиди и жди, как лох у парадной.
try {
value++; // А вот тут наша священная критическая секция. Теперь она в безопасности.
} finally {
lock.unlock(); // ОСВОБОЖДАЕМ в finally — это святое, иначе все зависнут и будут тебя проклинать!
}
}
public int getValue() {
lock.lock();
try {
return value;
} finally {
lock.unlock();
}
}
}
2. На уровне базы данных (JDBC)
Тут уже настоящая магия, или, скорее, криминал. Используем SELECT ... FOR UPDATE. Эта штука говорит базе данных: «Слушай, сука, вот эту строчку я пометил. Пока я с ней не разберусь — все остальные пусть идут нахуй и ждут».
Вот тебе живой пример с JDBC:
Connection conn = dataSource.getConnection();
try {
conn.setAutoCommit(false); // Поехали, начинаем транзакцию. Точка невозврата.
// 1. ПЕССИМИСТИЧЕСКИЙ ЗАХВАТ строки, блядь!
String selectSQL = "SELECT balance FROM accounts WHERE id = ? FOR UPDATE";
PreparedStatement selectStmt = conn.prepareStatement(selectSQL);
selectStmt.setInt(1, accountId);
ResultSet rs = selectStmt.executeQuery();
// Всё. Строка с accountId теперь в наших оковах. Остальные транзакции могут только плакать.
// 2. Читаем и колдуем над данными
int currentBalance = 0;
if (rs.next()) {
currentBalance = rs.getInt("balance");
}
rs.close();
selectStmt.close();
int newBalance = currentBalance - 100; // Списание, например.
// 3. Апдейтим эту уже захваченную и беспомощную строку
String updateSQL = "UPDATE accounts SET balance = ? WHERE id = ?";
PreparedStatement updateStmt = conn.prepareStatement(updateSQL);
updateStmt.setInt(1, newBalance);
updateStmt.setInt(2, accountId);
updateStmt.executeUpdate();
updateStmt.close();
conn.commit(); // Ура! Фиксируем изменения и снимаем блокировку. Все свободны.
} catch (SQLException e) {
conn.rollback(); // Если что-то пошло не так — откатываемся, как будто ничего и не было. И блокировка тоже падает.
throw e;
} finally {
conn.setAutoCommit(true);
conn.close();
}
А теперь, чтобы ты не запутался, смотри сравнение с его вечно улыбающимся оптимистичным братаном:
| Аспект | Пессимистическая (Наш суровый чел) | Оптимистическая (Тот, кто верит в хорошее) |
|---|---|---|
| Философия | Конфликты — это норма жизни. Хватай первым, потом разбирайся. | Конфликты — редкость. Давай сначала поработаем, а в конце проверим, не обосрал ли кто наш результат. |
| Механизм | synchronized, Lock, SELECT ... FOR UPDATE. |
Версионность (колонка version), Compare-and-Swap (CAS). |
| Производительность | Может проседать, потому что потоки тупо стоят и ждут, пока один мудак со своей блокировкой разберётся. | Летит как пуля, если все друг другу не мешают. Но если конфликт всё-таки случился — переделывай всё заново, ебать его в сраку. |
| Когда применять | Когда драка за ресурс — это каждый день. Высокая конкуренция, данные критичные до нельзя. | Когда все в основном читают, а пишут редко. Низкая конкуренция, все приличные люди. |
Короче, выбирай по обстановке. Если вокруг одни хамы и дикари — вешай замки. Если цивилизация и порядок — можно попробовать верить в лучшее. Но помни, чувак: доверия ебать ноль, а подозрение ёбаное чувствую всегда.