一、StampedLock概述
StampedLock
是读写锁的实现,对比 ReentrantReadWriteLock
主要不同是该锁不允许重入,多了乐观读的功能,使用上会更加复杂一些,但是具有更好的性能表现。StampedLock
的状态由版本和读写锁持有计数组成。 获取锁方法返回一个邮戳,表示和控制与锁状态相关的访问; 这些方法的“尝试”版本可能会返回特殊值 0 来表示获取锁失败。 锁释放和转换方法需要邮戳作为参数,如果它们与锁的状态不匹配则失败。本文描述 StampedLock
的基础使用场景,入门教程。
二、读写案例
在案例中,先使用 tryOptimisticRead
获取锁的邮戳,进行值修改后通过 validate
校验锁版本是否正确,此时并没有进行锁获取,这是一种乐观的模式。如果校验发现锁版本已被修改,则可以通过 readLock
以获取共享锁,然后重新进行数据读取。
package com.nineya.test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.StampedLock;
public class StampedLockTest {
private int num;
private int numLock;
StampedLock lock = new StampedLock();
public int getNum() {
return num;
}
public void read() {
int a = getNum();
int b = getNum();
if (a != b) {
System.out.println("num 改变: a = " + a + ", b = " + b);
}
}
public void write() {
num++;
}
public int getNumLock() {
return numLock;
}
public void readLock(){
long s = lock.tryOptimisticRead();
int a = getNumLock();
int b = getNumLock();
if (!lock.validate(s)) {
long state = lock.readLock();
a = getNumLock();
b = getNumLock();
lock.unlockRead(state);
}
if (a != b) {
System.out.println("numLock 改变: a = " + a + ", b = " + b);
}
}
public void writeLock() {
long state = lock.writeLock();
numLock++;
lock.unlockWrite(state);
}
public void print() {
System.out.println("num = " + num + ", numLock = " + numLock);
num = 0;
numLock = 0;
}
public static void main(String[] args) throws InterruptedException {
StampedLockTest main = new StampedLockTest();
ExecutorService executor = Executors.newCachedThreadPool();
for (int j = 0; j < 5; j++) {
for (int i = 0; i < 1000; i++) {
executor.execute(main::write);
executor.execute(main::writeLock);
executor.execute(main::read);
executor.execute(main::readLock);
}
Thread.sleep(1000);
main.print();
}
}
}
程序运行结果如下:
num 改变: a = 63, b = 64
num 改变: a = 92, b = 93
num 改变: a = 535, b = 536
num 改变: a = 939, b = 940
num = 999, numLock = 1000
num 改变: a = 134, b = 135
num = 998, numLock = 1000
num = 998, numLock = 1000
num = 999, numLock = 1000
num = 999, numLock = 1000
三、所有接口方法
方法 | 说明 |
---|---|
long writeLock() | 获取独占锁,如果该锁被另一个线程保持,则阻塞线程,直到拿到锁并返回邮戳。 |
long tryWriteLock() | 尝试获取独占锁,非公平,不阻塞线程。如果获取锁成功则返回邮戳,否则返回 0。 |
long tryWriteLock(long time, TimeUnit unit) throws InterruptedException | 在给定的等待时间内尝试获取独占锁,获取成功则立即返回邮戳。超过等待时间获取失败或者接收到中断信号则返回 0。 |
long writeLockInterruptibly() throws InterruptedException | 调用后一直阻塞到获得独占锁,但是接受中断信号。 |
long readLock() | 获取共享锁,如果该锁被另一个线程保持,则阻塞线程,直到拿到锁并返回邮戳。 |
long tryReadLock() | 尝试获取共享锁,非公平,不阻塞线程。如果获取锁成功则返回邮戳,否则返回 0。 |
tryReadLock(long time, TimeUnit unit) throws InterruptedException | 在给定的等待时间内尝试获取共享锁,获取成功则立即返回邮戳。超过等待时间获取失败或者接收到中断信号则返回 0。 |
long readLockInterruptibly() throws InterruptedException | 调用后一直阻塞到获得共享锁,但是接受中断信号。 |
long tryOptimisticRead() | 如果当前未持有独占锁则返回当前锁版本作为邮戳,用于在以后验证状态,如果已持有独占锁则返回0,用于乐观锁。 |
boolean validate(long stamp) | 如果自给定邮戳获取后未获取过独占锁(独占锁状态码未改变),则返回 true。 如果状态为 0,则始终返回 false。 |
void unlockWrite(long stamp) | 如果锁状态与给定的邮戳匹配,则释放独占锁。 |
void unlockRead(long stamp) | 如果锁状态与给定的邮戳匹配,则释放共享锁。 |
void unlock(long stamp) | 如果锁状态与给定的邮戳匹配,则释放锁的相应模式。 |
long tryConvertToWriteLock(long stamp) | 验证当前锁版本和锁持有状态和给定的邮戳是否匹配,如果不匹配、邮戳的锁状态有误或当前持有多个共享锁则返回 0。匹配时则分三种情况,当前未持有锁则获取独占锁,当前持有独占锁则不进行操作,当前仅持有一个共享锁则释放共享锁获取独占锁,最终返回独占锁的邮戳。 |
long tryConvertToReadLock(long stamp) | 验证当前锁版本和锁持有状态和给定的邮戳是否匹配,如果不匹配或者邮戳的锁状态有误则返回 0。匹配时则分三种情况,当前未持有锁则获取共享锁,当前持有独占锁则释放独占锁获取共享锁,当前持有共享锁则不进行操作,最终返回共享锁的邮戳。 |
long tryConvertToOptimisticRead(long stamp) | 验证当前锁版本和锁持有状态和给定的邮戳是否匹配,如果匹配则进行一次锁释放,如果不匹配或者邮戳的锁状态有误则返回 0。该方法的逻辑和 unlock 方法的逻辑相似,如果当前未持有锁就直接返回锁版本,如果持有锁则进行一次锁释放,再返回锁版本。 |
boolean tryUnlockWrite() | 如果持有独占锁则释放锁,不需要邮戳。 此方法对于错误后的恢复可能很有用。 |
boolean tryUnlockRead() | 如果持有共享锁则进行一次锁释放,不需要邮戳。 此方法对于错误后的恢复可能很有用。 |
boolean isWriteLocked() | 如果当前持有独占锁,则返回true 。 |
boolean isReadLocked() | 如果当前持有共享锁,则返回true 。 |
int getReadLockCount() | 查询当前持有的共享锁计数。 该方法设计用于监视系统状态,而不是用于同步控制。 |
Lock asReadLock() | 返回此 StampedLock 的普通Lock视图,其中Lock.lock方法映射到readLock ,其他方法也类似。 返回的 Lock 不支持Condition ; 方法Lock.newCondition()抛出UnsupportedOperationException。 |
Lock asWriteLock() | 返回此 StampedLock 的普通Lock视图,其中Lock.lock方法映射到writeLock ,其他方法也类似。 返回的 Lock 不支持Condition ; 方法Lock.newCondition()抛出UnsupportedOperationException。 |
ReadWriteLock asReadWriteLock() | 返回此 StampedLock 的ReadWriteLock视图,其中ReadWriteLock.readLock()方法映射到asReadLock() , ReadWriteLock.writeLock()映射到asWriteLock() 。 |