Я работаю над кодом Java, мне нужно внедрить в него потоки. Я проходил через JAVA 8 API, и я узнал о Stamped Locks. Может ли кто-нибудь сказать мне, почему использовать StampedLocks в многопоточности?
Спасибо заранее.
Я работаю над кодом Java, мне нужно внедрить в него потоки. Я проходил через JAVA 8 API, и я узнал о Stamped Locks. Может ли кто-нибудь сказать мне, почему использовать StampedLocks в многопоточности?
Спасибо заранее.
StampedLock является альтернативой использованию ReadWriteLock (реализуется ReentrantReadWriteLock). Основные отличия между StampedLock и ReentrantReadWriteLock заключаются в следующем:
Итак, если у вас есть сценарий, в котором у вас есть конфликт (в противном случае вы можете использовать synchronized
или простой Lock
) и больше читателей, чем писателей, использование StampedLock может значительно повысить производительность.
Однако вы должны измерить производительность, основанную на вашем конкретном случае использования, прежде чем перейти к выводам.
Хайнц Кабуц написал в своем информационном бюллетене "StampedLocks" презентация о производительности.
Документация API для java.util.concurrent.locks.StampedLock говорит:
StampedLocks предназначены для использования в качестве внутренних утилит при разработке поточно-безопасных компонентов. Их использование зависит от знания внутренних свойств данных, объектов и методов, которые они защищают. Они не являются реентерабельными, поэтому заблокированные тела не должны вызывать другие неизвестные методы, которые могут попытаться повторно приобрести блокировки (хотя вы можете передать штамп другим методам, которые могут его использовать или преобразовать). Использование режимов блокировки чтения зависит от того, связаны ли соответствующие секции кода без побочных эффектов. Неопределенные оптимистические разделы чтения не могут вызывать методы, которые, как известно, не допускают потенциальных несоответствий. Штампы используют конечные представления и не являются криптографически безопасными (т.е. Допустимый штамп может быть допустимым). Значения штампа могут быть возвращены после (не раньше) одного года непрерывной работы. Штамп, удерживаемый без использования или проверки дольше, чем этот период, может не соответствовать правильному правилу. StampedLocks являются сериализуемыми, но всегда десериализуются в начальное разблокированное состояние, поэтому они не полезны для дистанционной блокировки.
например. -
class Point {
private double x, y;
private final StampedLock sl = new StampedLock();
void move(double deltaX, double deltaY) { // an exclusively locked method
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
double distanceFromOrigin() { // A read-only method
long stamp = sl.tryOptimisticRead();
double currentX = x, currentY = y;
if (!sl.validate(stamp)) {
stamp = sl.readLock();
try {
currentX = x;
currentY = y;
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
void moveIfAtOrigin(double newX, double newY) { // upgrade
// Could instead start with optimistic, not read mode
long stamp = sl.readLock();
try {
while (x == 0.0 && y == 0.0) {
long ws = sl.tryConvertToWriteLock(stamp);
if (ws != 0L) {
stamp = ws;
x = newX;
y = newY;
break;
}
else {
sl.unlockRead(stamp);
stamp = sl.writeLock();
}
}
} finally {
sl.unlock(stamp);
}
}
}
StampedLock поддерживает блокировку чтения и записи. В отличие от ReadWriteLock методы блокировки StampedLock возвращают штамп, представленный длинным значением. Вы можете использовать эти штампы, чтобы либо отпустить блокировку, либо проверить, действительно ли блокировка действительна. Кроме того, штампованные замки поддерживают другой режим блокировки, называемый оптимистичной блокировкой.
ExecutorService executor = Executors.newFixedThreadPool(2);
Map<String, String> map = new HashMap<>();
StampedLock lock = new StampedLock();
executor.submit(() -> {
long stamp = lock.writeLock();
try {
Thread.sleep(100);
map.put("test", "INDIA");
} catch (Exception e) {
} finally {
lock.unlockWrite(stamp);
}
});
Runnable readTask = () -> {
long stamp = lock.readLock();
try {
System.out.println(map.get("test"));
Thread.sleep(100);
} catch (Exception e) {
} finally {
lock.unlockRead(stamp);
}
};
executor.submit(readTask);
executor.submit(readTask);
Получение блокировки чтения или записи через readLock() или writeLock() возвращает штамп, который позже используется для разблокировки в блоке finally. Имейте в виду, что штампованные блокировки не реализуют реентерабельные характеристики. Каждый вызов блокировки возвращает новый штамп и блокирует, если блокировка недоступна, даже если тот же поток уже содержит блокировку. Поэтому вы должны уделять особое внимание, чтобы не вступать в тупики.
executor.submit(() -> {
long stamp = lock.tryOptimisticRead();
try {
System.out.println("Optimistic Lock Valid: " + lock.validate(stamp));
Thread.sleep(100);
System.out.println("Optimistic Lock Valid: " + lock.validate(stamp));
Thread.sleep(1000);
System.out.println("Optimistic Lock Valid: " + lock.validate(stamp));
} catch (Exception e) {
} finally {
lock.unlock(stamp);
}
});
executor.submit(() -> {
long stamp = lock.writeLock();
try {
System.out.println("Write Lock acquired");
Thread.sleep(100);
} catch (Exception e) {
} finally {
lock.unlock(stamp);
System.out.println("Write done");
}
});
Оптимистичная блокировка чтения получается путем вызова метода tryOptimisticRead(), который всегда возвращает штамп без блокировки текущего потока, независимо от того, действительно ли блокировка доступна. Если активная блокировка записи активна, возвращаемый штамп равен нулю. Вы всегда можете проверить, действителен ли штамп, вызвав lock.validate(штамп).
В дополнение к @assylias ответ:
StampedLock поддерживает обновление readLock до writeLock с использованием метода tryConvertToWriteLock(long)
:
Этот класс также поддерживает методы, которые условно обеспечивают преобразования в трех режимах. Например, метод tryConvertToWriteLock (long) пытается "обновить" режим, возвращая допустимый штамп записи, если (1) уже находится в режиме записи (2) в режиме чтения, и нет других читателей или (3) в оптимистическом режиме и замок доступен. Формы этих методов разработаны, чтобы помочь уменьшить часть раздувания кода, которое иначе происходит в проектах на основе повторов.
примеры кода:
class Point {
private double x, y;
private final StampedLock sl = new StampedLock();
// an exclusively locked method
void move(double deltaX, double deltaY) {
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
// a read-only method
// upgrade from optimistic read to read lock
double distanceFromOrigin() {
long stamp = sl.tryOptimisticRead();
try {
retryHoldingLock: for (;; stamp = sl.readLock()) {
if (stamp == 0L)
continue retryHoldingLock;
// possibly racy reads
double currentX = x;
double currentY = y;
if (!sl.validate(stamp))
continue retryHoldingLock;
return Math.hypot(currentX, currentY);
}
} finally {
if (StampedLock.isReadLockStamp(stamp))
sl.unlockRead(stamp);
}
}
// upgrade from optimistic read to write lock
void moveIfAtOrigin(double newX, double newY) {
long stamp = sl.tryOptimisticRead();
try {
retryHoldingLock: for (;; stamp = sl.writeLock()) {
if (stamp == 0L)
continue retryHoldingLock;
// possibly racy reads
double currentX = x;
double currentY = y;
if (!sl.validate(stamp))
continue retryHoldingLock;
if (currentX != 0.0 || currentY != 0.0)
break;
stamp = sl.tryConvertToWriteLock(stamp);
if (stamp == 0L)
continue retryHoldingLock;
// exclusive access
x = newX;
y = newY;
return;
}
} finally {
if (StampedLock.isWriteLockStamp(stamp))
sl.unlockWrite(stamp);
}
}
// Upgrade read lock to write lock
void moveIfAtOrigin(double newX, double newY) {
long stamp = sl.readLock();
try {
while (x == 0.0 && y == 0.0) {
long ws = sl.tryConvertToWriteLock(stamp);
if (ws != 0L) {
stamp = ws;
x = newX;
y = newY;
break;
}
else {
sl.unlockRead(stamp);
stamp = sl.writeLock();
}
}
} finally {
sl.unlock(stamp);
}
}
}