概述
带有三种模式的基于能力的锁,用于控制读/写访问。StampedLock 的状态由版本和模式组成。锁定获取方法返回一个戳记,该戳记代表并控制与锁状态相关的访问;这些方法的“尝试”版本可能会返回特殊值 0,表示无法获得访问。锁释放和转换方法需要戳记作为参数,如果它们与锁的状态不匹配,则失败。这三种模式是:
- 写入。方法 writeLock 可能会阻塞等待独占访问,返回一个戳记,该戳记可以在方法 unlockWrite 中使用来释放锁。还提供了未定时和定时版本的 tryWriteLock。当锁以写模式持有时,无法获得读锁,并且所有乐观读验证都将失败。
- 读取。方法 readLock 可能会阻塞等待非独占访问,返回一个戳记,该戳记可以在方法 unlockRead 中使用来释放锁。还提供了未定时和定时版本的 tryReadLock。
- 乐观读取。这种模式可以被认为是读锁的极弱版本,在任何时候都可能被写入者打破。在短的只读代码段中使用乐观读取模式通常会减少争用并提高吞吐量。但是,它的使用本质上是脆弱的。乐观读取部分应仅读取字段并将它们保存在局部变量中以供稍后使用。在乐观读取模式下读取的字段可能不一致,因此使用仅适用于您对数据表示足够熟悉,可以检查一致性或重复调用验证(validate)方法。例如,这些步骤通常在首次读取对象或数组引用,然后访问其中一个字段,元素或方法时是必需的。
这个类还支持有条件地在三种模式之间转换的方法。例如,方法 tryConvertToWriteLock 尝试“升级”模式,如果(1)已经在写入模式(2)在读取模式且没有其他读者或(3)在乐观读取模式且锁可用,则返回有效的写入戳记。这些方法的形式旨在帮助减少基于重试的设计中的一些代码膨胀。
StampedLocks 是为开发线程安全组件时内部使用而设计的。它们的使用依赖于对保护的数据、对象和方法的内部属性的了解。它们不是可重入的,因此加锁的主体不应调用其他可能尝试重新获取锁的未知方法(尽管您可以将戳记传递给其他可以使用或转换它的方法)。使用读锁模式的使用依赖于关联的代码段没有副作用。未验证的乐观读取部分不能调用可能不容忍潜在不一致性的未知方法。戳记使用有限表示,不是加密安全的(即,有效的戳记可能可以猜测)。在连续运行一年后(不晚于此时间),戳记可能会回收。如果一个戳记在此期间没有使用或验证就被保留,则可能无法正确验证。StampedLocks 可以序列化,但始终反序列化为初始未锁定状态,因此他们不适用于远程锁定。
StampedLocks的调度策略不一定总是倾向于优先读取者或写入者。所有的“尝试”方法都是尽力而为的,不一定符合任何调度或公平策略。任何用于获取或转换锁的“尝试”方法的零返回值并不携带有关锁状态的任何信息;随后的调用可能会成功。因为它支持多个锁模式的协调使用,所以这个类没有直接实现Lock或ReadWriteLock接口。但是,在需要仅相关功能集的应用程序中,可以将StampedLock视为ReadLock,asWriteLock或asReadWriteLock。
内存同步。 在任何模式下成功锁定的方法具有与锁定操作相同的内存同步效果,如《Java语言规范》第17章中所述。 在写模式下成功解锁的方法具有与解锁操作相同的内存同步效果。 在乐观读取用法中,在最近的写模式解锁操作之前的操作在validate返回true之前被保证发生在之后的操作之前; 否则,无法保证在tryOptimisticRead和validate之间的读取获得一致的快照。 示例用法。 以下是维护简单二维点的类的一些用法习惯用法。 示例代码演示了一些try / catch约定,即使它们在此处并不严格必要,因为它们的体内不会发生异常。
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 moveIfAtOrigin2(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);
}
}
}
分享到: