提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
stampLock的大致功能预计他的读锁源码解析
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录- 系列文章目录
- 前言
- 一、stampLock是什么?以及其中的重要常量
- 二、1.写锁代码以及源码解析
- 2.读锁代码
- 读锁源码部分解析
- 3.读锁升级代码
- 4.核心方法
- 总结
前言
提示:这里可以添加本文要记录的大概内容:
前不久和朋友讨论了一波stamplock的功能以及他的锁重入问题;于是乎跟他一起看起了源码;很折磨!!!
提示:以下是本篇文章正文内容,下面案例可供参考
一、stampLock是什么?以及其中的重要常量stampLock其实可以简单地看成readWriteLock的一种加强
对于读锁的加强,他的读锁从原本的悲观锁变成了读锁。
要读懂源码 首先要把代码中的常用变量给理解清楚 并大概的记下来 才容易理解源码内容
废话不多看源码
引用的关键值
//类序列号
private static final long serialVersionUID = -6001602636862214147L;
private static final int NCPU = Runtime.getRuntime().availableProcessors();
private static final int SPINS = (NCPU > 1) ? 1 << 6 : 1;
private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 1;
private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 1;
private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1
private static final int LG_READERS = 7;
// Values for lock state and stamp operations
private static final long RUNIT = 1L;
private static final long WBIT = 1L << LG_READERS;
private static final long RBITS = WBIT - 1L;
private static final long RFULL = RBITS - 1L;
private static final long ABITS = RBITS | WBIT;
private static final long SBITS = ~RBITS; // note overlap with ABITS
private static final long ORIGIN = WBIT << 1;
// 来自取消需求方法的特殊值 一边响应者可以返回IE(ioexception)
private static final long INTERRUPTED = 1L;
// 状态标记的值
private static final int WAITING = -1;
private static final int CANCELLED = 1;
// 标记的模式 是int类型而不是boolean类型去允许计算的
private static final int RMODE = 0;
private static final int WMODE = 1;
static final class WNode {
volatile WNode prev;
volatile WNode next;
volatile WNode cowait; // 连接读者列表
volatile Thread thread; // 当可能停止时不为零
volatile int status; // 当0时 暂停或者取消
final int mode; // RMODE / WMODE
WNode(int m, WNode p) { mode = m; prev = p; }
}
private transient volatile WNode whead;
private transient volatile WNode wtail;
// views
transient ReadLockView readLockView;
transient WriteLockView writeLockView;
transient ReadWriteLockView readWriteLockView;
private transient volatile long state;
private transient int readerOverflow;
重要的中间方法1:比较替换
private boolean casState(long expectedValue, long newValue) {
return STATE.compareAndSet(this, expectedValue, newValue);
}
二、1.写锁代码以及源码解析重要的中间方法
官方示例代码 ----提供大概的理解以及应用
public class StampedLockDemo {
//一个点的x,y坐标
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);//释放写锁
}
}
当然重要的还是要理解源码底层的意思
acquireWrite代码部分解析
private long acquireWrite(boolean interruptible, long deadline) {
WNode node = null, p;
for (int spins = -1;;) { // spin while enqueuing
long m, s, ns;
if ((m = (s = state) & ABITS) == 0L) {
if ((ns = tryWriteLock(s)) != 0L)
return ns;
}
else if (spins < 0)
spins = (m == WBIT && wtail == whead) ? SPINS : 0;
else if (spins > 0) {
--spins;
Thread.onSpinWait();
}
else if ((p = wtail) == null) { // initialize queue
WNode hd = new WNode(WMODE, null);
if (WHEAD.weakCompareAndSet(this, null, hd))
wtail = hd;
}
else if (node == null)
node = new WNode(WMODE, p);
else if (node.prev != p)
node.prev = p;
else if (WTAIL.weakCompareAndSet(this, p, node)) {
p.next = node;
break;
}
}
..........
这里的代码(jdk9)相比与jdk8代码相差比较大
使用的是系统nanotime进行对deadlie进行设置 当超过这个时间 读操作还没有进行完成就会对读操作打断 转而进行写操作的判断
jdk8相关代码
这里是使next值大于Rfull 这样就会跳进一个写操作 而当大于rFull时 所有的读操作都会出错 造成没办法进行读 转而进行写操作
代码如下(示例):
//【乐观读锁】
double distanceFromOrigin() { // A read-only method
long stamp = sl.tryOptimisticRead();
//这里就是读操作,读取x和y,因为读取x时,y可能被写了新的值,所以下面需要判断
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);
}
读锁源码部分解析
读锁的重要操作
private long acquireRead(boolean interruptible, long deadline) {
boolean wasInterrupted = false;
WNode node = null, p;
for (int spins = -1;;) {
WNode h;
if ((h = whead) == (p = wtail)) {
for (long m, s, ns;;) {
if ((m = (s = state) & ABITS) < RFULL ?
casState(s, ns = s + RUNIT) :
(m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
if (wasInterrupted)
Thread.currentThread().interrupt();
return ns;
}
else if (m >= WBIT) {
if (spins > 0) {
--spins;
Thread.onSpinWait();
}
else {
if (spins == 0) {
WNode nh = whead, np = wtail;
if ((nh == h && np == p) || (h = nh) != (p = np))
break;
}
spins = SPINS;
}
}
}
}
其中的 if ((m = (s = state) & ABITS) < RFULL ? 模块中
当当m>WBIT 时有两种情况
情况一自旋(spins)>0 即可理解为 当写线程出发的时候 会打断写线程 继续重新尝试读线程
情况二 自旋(spins)==0 即写线程重试不允许了 则退出读 执行写
读锁操作
public long readLock() {
long s, next;
// 通过 acquiredRead 方法因对常见的未认证的情况
//执行条件
return (whead == wtail
&& ((s = state) & ABITS) < RFULL
&& casState(s, next = s + RUNIT))
? next
: acquireRead(false, 0L);
}
读操作时: casState(s, next = s + RUNIT))//读操作时该处+1L,来进行casState状态比较 ——如若确定为读操作的锁 ,则state值会小于rfull 则返回一个相同的s值此时stamp值不变
读锁的尝试获取操作
public long tryReadLock() {
long s, m, next;
while ((m = (s = state) & ABITS) != WBIT) {
if (m < RFULL) {
if (casState(s, next = s + RUNIT))
return next;
}
else if ((next = tryIncReaderOverflow(s)) != 0L)
return next;
}
return 0L;
}
3.读锁升级代码
//读锁升级为写锁
void moveIfAtOrigin(double newX, double newY) { // upgrade
// 读锁(这里可用乐观锁替代)
long stamp = sl.readLock();
try {
//循环,检查当前状态是否符合
while (x == 0.0 && y == 0.0) {
long ws = sl.tryConvertToWriteLock(stamp);
//如果写锁成功
if (ws != 0L) {
stamp = ws;// 替换stamp为写锁戳
x = newX;//修改数据
y = newY;
break;
}
//转换为写锁失败
else {
//释放读锁
sl.unlockRead(stamp);
//获取写锁(必要情况下阻塞一直到获取写锁成功)
stamp = sl.writeLock();
}
}
} finally {
//释放锁(可能是读/写锁)
sl.unlock(stamp);
}
}
}
4.核心方法 总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。



