文章目录
StampedLock
是什么?
源码中的解释是:
一种基于功能的锁,具有三种模式来控制读写访问。 StampedLock的状态由版本和模式组成。锁获取方法返回一个表示并控制相对于锁状态的访问的stamp;这些方法的“尝试”版本可能会返回特殊值零,以表示无法获取访问权限。锁释放和转换方法需要使用图章作为参数,如果它们与锁的状态不匹配,则会失败。
三种模式分别是:写锁、读锁、乐观读(可以升级的锁)。
注意:是一种不可重入的锁。
在读多写少的场景下可以提高吞吐量
结构
类图
组成
一个等待队列
/** 等待节点队列 */
static final class WNode {
volatile WNode prev;
volatile WNode next;
// 链接的读者列表
volatile WNode cowait; // list of linked readers
// 可能停放时为非空
volatile Thread thread; // non-null while possibly parked
volatile int status; // 0, WAITING, or CANCELLED
final int mode; // RMODE or WMODE
WNode(int m, WNode p) { mode = m; prev = p; }
}
/** Head of CLH queue */
private transient volatile WNode whead;
/** Tail (last) of CLH queue */
private transient volatile WNode wtail;
一个读锁
// 读锁
final class ReadLockView implements Lock {
public void lock() { readLock(); }
public void lockInterruptibly() throws InterruptedException {
readLockInterruptibly();
}
public boolean tryLock() { return tryReadLock() != 0L; }
public boolean tryLock(long time, TimeUnit unit)
throws InterruptedException {
return tryReadLock(time, unit) != 0L;
}
public void unlock() { unstampedUnlockRead(); }
public Condition newCondition() {
throw new UnsupportedOperationException();
}
}
一个写锁
// 写锁
final class WriteLockView implements Lock {
public void lock() { writeLock(); }
public void lockInterruptibly() throws InterruptedException {
writeLockInterruptibly();
}
public boolean tryLock() { return tryWriteLock() != 0L; }
public boolean tryLock(long time, TimeUnit unit)
throws InterruptedException {
return tryWriteLock(time, unit) != 0L;
}
public void unlock() { unstampedUnlockWrite(); }
public Condition newCondition() {
throw new UnsupportedOperationException();
}
}
一个读写锁
// 读写锁
final class ReadWriteLockView implements ReadWriteLock {
public Lock readLock() { return asReadLock(); }
public Lock writeLock() { return asWriteLock(); }
}
Unsafe类
// Unsafe mechanics
private static final sun.misc.Unsafe U;
private static final long STATE;
private static final long WHEAD;
private static final long WTAIL;
private static final long WNEXT;
private static final long WSTATUS;
private static final long WCOWAIT;
private static final long PARKBLOCKER;
static {
try {
// 获取Unsafe类的字段值
U = sun.misc.Unsafe.getUnsafe();
Class<?> k = StampedLock.class;
Class<?> wk = WNode.class;
STATE = U.objectFieldOffset
(k.getDeclaredField("state"));
WHEAD = U.objectFieldOffset
(k.getDeclaredField("whead"));
WTAIL = U.objectFieldOffset
(k.getDeclaredField("wtail"));
WSTATUS = U.objectFieldOffset
(wk.getDeclaredField("status"));
WNEXT = U.objectFieldOffset
(wk.getDeclaredField("next"));
WCOWAIT = U.objectFieldOffset
(wk.getDeclaredField("cowait"));
Class<?> tk = Thread.class;
PARKBLOCKER = U.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
} catch (Exception e) {
throw new Error(e);
}
}
和其他的一些变量。
/** 获取当前计算机的CPU核数 */
private static final int NCPU = Runtime.getRuntime().availableProcessors();
/** Maximum number of retries before enqueuing on acquisition */
// 64:0
private static final int SPINS = (NCPU > 1) ? 1 << 6 : 0;
/** Maximum number of retries before blocking at head on acquisition */
// 1024:0
private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 0;
/** Maximum number of retries before re-blocking */
// 65536:0
private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 0;
/** 自旋锁溢出yield的时间 */
private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1
/** The number of bits to use for reader count before overflowing */
private static final int LG_READERS = 7;
// Values for lock state and stamp operations
// 锁定状态和标记操作的值
private static final long RUNIT = 1L;
// 128
private static final long WBIT = 1L << LG_READERS;
// 127
private static final long RBITS = WBIT - 1L;
// 126
private static final long RFULL = RBITS - 1L;
// 255
private static final long ABITS = RBITS | WBIT;
// ~取反运算符
private static final long SBITS = ~RBITS; // note overlap with ABITS
// 锁定状态的初始值;避免故障值为零
// 256
private static final long ORIGIN = WBIT << 1;
// Special value from cancelled acquire methods so caller can throw IE
// 取消获取方法中的特殊值,因此调用方可以抛出IE
private static final long INTERRUPTED = 1L;
// Values for node status; order matters
private static final int WAITING = -1;
private static final int CANCELLED = 1;
// Modes for nodes (int not boolean to allow arithmetic)
// 节点的模式(不为布尔值以允许算术)
private static final int RMODE = 0;
private static final int WMODE = 1;
原理
其中相较于AQS多的一个cowait节点是读模式使用该节点的链表。thread是挂起的线程。
进来一个线程:如果队尾是读节点,不会连接到队尾,而是读节点的cowait()节点;如果队尾是写节点,直接连接到队尾。
**唤醒线程:**唤醒队首节点,如果是读节点,cowait也会被唤醒。
案例
public class StampedLockTest {
public static void main(String[] args) {
StampedLockTest lockTest = new StampedLockTest();
//new Thread(()->{
// for (int i = 0; i < 20; i++) {
//
// System.out.println("read1:"+lockTest.distanceFromOrigin());
//
// }
//},"read1").start();
new Thread(()->{
for (double i = 1; i <= 10; i++) {
lockTest.moveIfAtOrigin(i,i);
}
},"升级锁").start();
//new Thread(()->{
// for (int i = 0; i < 20; i++) {
// try {
// System.out.println("read2:"+lockTest.distanceFromOrigin());
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
//},"read2").start();
new Thread(()->{
for (int i = 0; i < 2000000000; i++) {
lockTest.move(-1,-1);
}
},"write1").start();
new Thread(()->{
for (int i = 0; i < 2000000000; i++) {
lockTest.move(-1,-1);
}
},"write2").start();
}
private double x, y;
private final StampedLock sl = new StampedLock();
void move(double deltaX, double deltaY) {
long stamp = sl.writeLock();
System.out.println("写锁");
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) {
long stamp = sl.readLock();
try {
System.out.println(x+"----"+y);
// 只有当变量满足这个条件时,才会升级为写锁
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);
}
}
}
图片转自:https://www.jianshu.com/p/bfd5d2321cc0