这是我参与8月更文挑战的第31天,活动详情查看:8月更文挑战
读写锁的改进
- java8 引入的新的锁机制,类似无锁的操作
- 提供一种乐观的读策略,不会产生大量读,写的"饥饿"
/**
* @Author: xiaoff
* @Date: 2021/8/29 21:17
*/
public class Point {
// x y 为坐标
private double x,y;
// 排他锁
private final StampedLock s = new StampedLock();
void move(double dx,double dy) {
long stamp = s.writeLock();
try {
x += dx;
y += dy;
} finally {
s.unlockWrite(stamp);
}
}
double distanceFromOrigin() {
long stamp = s.tryOptimisticRead();
double dx = x,dy = y;
if (!s.validate(stamp)) {
stamp = s.readLock();
try {
dx = x;
dy = y;
} finally {
s.unlockRead(stamp);
}
}
return Math.sqrt(dx * dx + dy * dy);
}
}
- point是一个点的类,有xy元素
- 其中
tryOptimisticRead为乐观读的方法 - stamp是一个时间戳,来返回一次获取锁的凭证
validate()用来判断stamp是否在读的或城中发生修改过- 如果没被修改,那么此次读有效
- 否则就出现了脏读
- 其中
小的陷阱
StampedLock的内部是类似于CAS的死循环反复尝试- 使用的是Unsafe.park()来挂起线程
- 而且StampedLock中没有中断逻辑,就会可能出现死循环不能退出的情况
实现思想
- StampedLock它的内部是基于CLH队列自旋锁🔐,而且可以保证先进先出
- CLH锁的基本思想就是,维护一个等待队列,所有申请锁但是没有成功的线程都记录到这个队列中
- 每一个喜爱昵称保存一个标记位,来判断当前线程是否已经释放锁
- 来看源码!
- 其中
- cowait 是读节点链表
- thread 是当可能被暂停时非空
- whead 是CLH的队列头部
- wtail 是CLH队列的尾部
LongAdder
- 在java8没有引入的时候,想必大家很熟悉
AtomicInteger- 而它的缺点就是不断尝试修改目标值,直到修改成功
- 那么这样的做法是十分损耗性能的
- 因此java8引入了
LongAdder,在查看其源码的时候可以清除的看到满屏的cell- 就是将所有的数据先记录在base变量中
- 一旦base冲突,那么就会初始化
cell数组
- 最开始cell数组为null,base增加,如果有了冲突boolean的变量就会为true
- 如果当前数组为空,那么也进入
longAccumulate - 而
longAccumulate就是将cell数组进行扩容,减少冲突
- 如果当前数组为空,那么也进入
源码我们需要理解的是底层思想,而不是去背下来,冲冲冲🤪