带你了解「StampedLock」

181 阅读2分钟

这是我参与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锁的基本思想就是,维护一个等待队列,所有申请锁但是没有成功的线程都记录到这个队列中
    • 每一个喜爱昵称保存一个标记位,来判断当前线程是否已经释放锁
    • 来看源码!

图片.png

  • 其中
    1. cowait 是读节点链表
    2. thread 是当可能被暂停时非空
    3. whead 是CLH的队列头部
    4. wtail 是CLH队列的尾部

LongAdder

  • 在java8没有引入的时候,想必大家很熟悉AtomicInteger
    • 而它的缺点就是不断尝试修改目标值,直到修改成功
    • 那么这样的做法是十分损耗性能的
  • 因此java8引入了LongAdder,在查看其源码的时候可以清除的看到满屏的cell
    • 就是将所有的数据先记录在base变量中
    • 一旦base冲突,那么就会初始化cell数组

图片.png

  • 最开始cell数组为null,base增加,如果有了冲突boolean的变量就会为true
    • 如果当前数组为空,那么也进入longAccumulate
    • longAccumulate就是将cell数组进行扩容,减少冲突

源码我们需要理解的是底层思想,而不是去背下来,冲冲冲🤪