JDK8中StampedLock的使用

StampedLock与ReentrayLock相比,使用上要复杂些,并且不可重入,但是在某些场景下性能比重入锁好。

StampedLock为共享变量的读写提供三种模式:乐观读,读锁,写锁StampedLock中获取锁的方法成功后会返回一个数字邮戳,用来控制对锁的访问及释放,获取失败会一直阻塞。带try前缀的锁获取方法获取锁失败会返回0。调用锁释放与锁转换方法需要传入获取锁时得到的数字邮戳,如果不匹配会失败。

下面详细解释下三种锁模式:

  • 写入锁:writeLock方法会获取对资源的独占访问权,如果获取不到锁就一直阻塞,返回的数字邮戳可以传入unlockWrite方法释放该写入锁。JDK还提供了可以传入超时时间的tryWriteLock方法用来获取写入锁。当StampedLock处于写入锁状态时,所有读取锁、乐观读验证都会失败。
  • 读取锁:readLock会一直阻塞直到资源的独占访问权被释放,其实就是写锁被释放,返回的数字邮戳可以传入unlockRead方法释放该读取锁。JDK还提供了可以传入超时时间的tryReadLock方法用来获取写入锁。
  • 乐观读:没有写入锁时,tryOptimisticRead方法会返回一个非零的数字邮戳,validate方法会验证该数字邮戳是否有效,两个方法执行期间有别的线程获取了写入锁,则邮戳失效。乐观读可以看做是一个弱化版的读取锁,可以被写入锁任意打断。对短只读代码片段使用乐观读模式通常会减少锁竞争并提高吞吐量。然而,其使用本身是脆弱的。乐观读取部分应该只读取字段,并将它们保存在局部变量中,以便在验证之后使用。在乐观读模式下读取的字段可能前后不一致,因此只有足够熟悉字段含义,直到如何检查一致性或调用validate方法,再应用此模式。例如,当读取对象或数组引用时,访问其字段、元素或方法之一,通常需要执行上述validate方法。

此类还支持在三种锁模式之间有条件地进行转换。例如,方法TryconvertToWriteLock尝试进行锁升级,如果

(1)已经处于写入锁模式

(2)处于读取锁模式且没有其他读取锁线程

(3)处于乐观读模式且锁可用,则返回有效的写入戳。
StampedLocks are designed for use as internal utilities in the development of thread-safe components. Their use relies on knowledge of the internal properties of the data, objects, and methods they are protecting. They are not reentrant, so locked bodies should not call other unknown methods that may try to re-acquire locks (although you may pass a stamp to other methods that can use or convert it). The use of read lock modes relies on the associated code sections being side-effect-free. Unvalidated optimistic read sections cannot call methods that are not known to tolerate potential inconsistencies. Stamps use finite representations, and are not cryptographically secure (i.e., a valid stamp may be guessable). Stamp values may recycle after (no sooner than) one year of continuous operation. A stamp held without use or validation for longer than this period may fail to validate correctly. StampedLocks are serializable, but always deserialize into initial unlocked state, so they are not useful for remote locking.
Like Semaphore, but unlike most Lock implementations, StampedLocks have no notion of ownership. Locks acquired in one thread can be released or converted in another.
The scheduling policy of StampedLock does not consistently prefer readers over writers or vice versa. All “try” methods are best-effort and do not necessarily conform to any scheduling or fairness policy. A zero return from any “try” method for acquiring or converting locks does not carry any information about the state of the lock; a subsequent invocation may succeed.
Because it supports coordinated usage across multiple lock modes, this class does not directly implement the Lock or ReadWriteLock interfaces. However, a StampedLock may be viewed asReadLock(), asWriteLock(), or asReadWriteLock() in applications requiring only the associated set of functionality.
Sample Usage. The following illustrates some usage idioms in a class that maintains simple two-dimensional points. The sample code illustrates some try/catch conventions even though they are not strictly needed here because no exceptions can occur in their bodies.

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);
} }
} Since:
1.8
< 11 >