前端性能优化,掌握行业实用、专业、前沿的解决方案

download:前端性能优化,掌握行业实用、专业、前沿的解决方案

StampedLock读写锁

“StampedLock简介”
StampedLock的状态由版本和模式组成。get lock方法返回一个表示和控制对锁状态的访问的戳。
StampedLock提供三种模式来控制访问锁:

写入模式

获取写锁,这是独占的。当锁处于写模式时,您无法获得读锁,并且所有乐观读验证都将失败。

writeLock():阻塞等待锁的独占获取,并返回一个戳。如果为0,则采集失败。
TryWriteLock():`尝试获取一个写锁并返回一个时间戳。如果为0,则采集失败。
Long trywritelock (长时间,时间单位单位):当试图获取一个独占写锁时,可以等待一个事件并返回一个stamp。如果为0,则采集失败。
long writelockinterrupt():尝试获取一个独占的写锁,它可以被中断并返回一个时间戳。如果为0,则采集失败。
UnlockWrite(long stamp):`释放独占写锁,并传入以前获取的stamp。
tryUnlockWrite():如果持有写锁,则该锁将在没有标记值的情况下被释放。这种方法对于出错后的恢复可能很有用。

long stamp = lock . write lock();
尝试{
....
}最后{
lock.unlockWrite(戳);
}

复制代码

读取模式

独占读锁之后的悲观之路。

readLock():阻塞等待获取非独占读锁,并返回一个stamp。如果为0,则采集失败。
TryReadLock():`尝试获取一个读锁并返回一个时间戳。如果为0,则采集失败。
长读锁(long time,时间单位单位):当试图获取读锁时,可以等待一个事件并返回一个时间戳。如果为0,则采集失败。
Readlockinterrupt():等待获取非独占读锁的块,它可以被中断并返回一个stamp。如果为0,则采集失败。
UnlockRead(long stamp):释放非独占读锁,并传入之前获取的stamp。
tryUnlockRead():如果持有读锁,释放持有一次,不需要stamp值。这种方法对于出错后的恢复可能很有用。

长戳= lock . read lock();
尝试{
....
}最后{
lock . unlock read(stamp);
}

复制代码

乐观阅读模式

乐观是指如果读操作多,写操作少,可以乐观地认为写和读同时发生的概率很小,可以使用完全读锁,不悲观。在读取数据后,程序可以通过写入来检查它是否被更改,然后采取后续措施(重新读取更改的信息或抛出异常)。这个小小的改进可以大大提高程序的吞吐量。
StampedLock支持tryOptimisticRead()方法。阅读后,进行盖章检查。如果检查通过,则意味着在此期间没有其他线程写入,数据可以安全使用。如果检查失败,需要重新获取读锁以确保数据一致性。

TryOptimisticRead():`返回一个可以在以后验证的戳,如果以独占方式锁定,则返回零。
boolean validate(long stamp):如果自给定的stamp发出后锁尚未被独占获取,则返回true。

long stamp = lock . trypositicread();
//检查戳记
如果(!lock.validate(stamp)){
//锁定升级
}

复制代码
此外,StampedLock提供api来实现上述三种转换方式:

`长tryConvertToWriteLock(长戳)'

如果锁定状态与给定的标记匹配,请执行下列操作之一。如果该标记指示持有写锁,则返回该标记。或者,如果是读锁,并且写锁可用,则释放读锁并返回写戳。或者,在乐观读取的情况下,写戳只有在立即可用时才返回。在所有其他情况下,该方法返回零。

` long tryConvertToReadLock(长戳)'

如果锁定状态与给定的标记匹配,请执行下列操作之一。如果标记指示持有写锁,则释放它并获得读锁。或者,如果是读锁,则返回它。或者,在乐观读取的情况下,只有当读取标记立即可用时,才会获得读取锁并返回读取标记。在所有其他情况下,该方法返回零。

长tryConvertToOptimisticRead(长戳)

如果锁的状态与给定的标记相匹配,那么如果标记指示锁被持有,则释放锁并返回观察标记。或者,如果是乐观阅读,验证后返回。在所有其他情况下,该方法都返回0,因此它作为“tryUnlock”的一种形式可能很有用。
演示示例
用下面的例子来演示StampedLock的用法。这个例子来自jdk中的javadoc。

@Slf4j
@数据
公共类点{
私有双x,y;
private final StampedLock sl = new StampedLock();

void move(double deltaX,double deltaY)抛出中断异常{
//涉及共享资源的修改,使用写锁排他操作。
long stamp = sl . write lock();
log.info("writeLock锁成功");
thread . sleep(500);
尝试{
x+= deltaX;
y+= deltaY;
}最后{
sl.unlockWrite(盖章);
log.info("解锁写锁成功");
}
}

/**
*使用乐观读锁访问共享资源。
*注意:乐观读锁需要将一个要操作的变量复制到方法栈中,以保证数据的一致性,其他写者在操作数据时可能已经修改了数据。
*而我们操作的是方法栈中的数据,也就是快照,所以返回最多的数据不是最新的数据,但是一致性还是有保证的。
*
* @返回
*/
double distanceFromOrigin()抛出InterruptedException {
long stamp = sl . trypositicread();//使用乐观读锁
log . info(" trypositicread锁成功");
//睡一秒钟
thread . sleep(1000);
double currentX = x,currentY = y;//将共享资源复制到本地方法堆栈中。
如果(!Sl.validate(stamp)) {//如果写锁被占用,可能会导致数据不一致,所以切换到正常的读锁模式。
log.info("验证戳记错误");
stamp = sl . read lock();
log.info("readLock成功");
尝试{
currentX = x;
currentY = y;
}最后{
sl.unlockRead(盖章);
log.info("解锁读取成功");
}
}
return math . sqrt(currentX * currentX+currentY * currentY);
}

void moveIfAtOrigin(double newX,double newY) { //升级
//可以从乐观模式而不是读取模式开始
长戳= sl . read lock();
尝试{
while (x == 0.0 && y == 0.0) {
long ws = sl . tryconverttowritelock(stamp);//读锁转换为写锁
如果(ws!= 0L) {
stamp = ws
x = newX
y = newY
打破;
}否则{
sl.unlockRead(盖章);
stamp = sl . write lock();
}
}
}最后{
sl.unlock(盖章);
}
}
}

复制代码

测试案例:

@测试
public void testStamped()引发InterruptedException {
Point Point = new Point();
point . setx(1);
point . sety(2);
//线程0执行了乐观读取。
Thread thread0 =新线程(()--> {
尝试{
//乐观地阅读
point . distance fromorigin();
} catch (InterruptedException e) {
e . printstacktrace();
}
},“thread-0”);
thread 0 . start();

thread . sleep(500);
//线程1执行写锁定
Thread thread1 =新线程(()--> {
//乐观地阅读
尝试{
point.move(3,4);
} catch (InterruptedException e) {
e . printstacktrace();
}
},“线程-1”);
thread 1 . start();

thread 0 . join();
thread 1 . join();
}

复制代码

结果:

![图片](https://P3-Jue Jin . byte img . com/tos-cn-I-k 3u 1 FB pfcp/913 e 41 e 58 a 104 a 83 BDA 9 aacb 224 a 567 c 6 ~ tplv-k 3u 1 FB pfcp-zoom-in-crop-mark:4550

性能比较
因为StampedLock的乐观读取模式和高性能高吞吐量,具体性能提升多少?
下图显示,与ReadWritLock相比,在一个线程的情况下,读取速度是4倍左右,写入速度是1倍。

![图片](https://P3-Jue Jin . byte img . com/tos-cn-I-k 3u 1 FB pfcp/c 6 ebfa 5c 54537 a 07 b 02973 b 74 b 44 ~ tplv-k 3u 1 FB pfcp-zoom-in-crop-mark:4533

下图显示,当有16个线程时,读性能是几十倍,写性能接近10倍:

![图片](https://P3-Jue Jin . byte img . com/tos-cn-I-k 3u 1 FB pfcp/31c 9 f 81 e 3c 5c 401484818 c 066 df 6908 f ~ tplv-k 3u 1 FB pfcp-zoom-in-crop-mark:4550

下图显示了吞吐量的提高:

![图片](https://P3-Jue Jin . byte img . com/tos-cn-I-k 3u 1 FB pfcp/00230309061 f 49 f 8957 da 62d 44 FDC 7 c 6 ~ tplv-k 3u 1 FB pfcp-zoom-in-crop-mark:44

那么这是否意味着“戳记锁”可以在所有方向上取代“ReentrantReadWriteLock”呢?答案是否定的,“戳记锁”相对于“ReentrantReadWriteLock”有以下两个问题:

不支持条件变量“Condition”。
支持不可重入

所以最终选择StampedLock还是ReentrantReadWriteLock取决于具体的业务场景。

摘要

本文主要介绍“盖章锁”的功能和使用。从原理上来说,虽然' Stamped Lock '没有像其他锁一样定义内部类来实现AQS框架,但是' Stamped Lock '的基本实现思想是使用CLH队列来管理线程,通过同步状态值来指示锁的状态和类型。

你可能感兴趣的:(性能优化)