zoukankan      html  css  js  c++  java
  • 第二部分:并发工具类18->StampedLock:比读写锁更快的锁

    1.StampedLock

    读多写少的场景,StampedLock性能比读写锁更好

    2.StampedLock 支持的三种锁模式

    读写锁,2种模式,读锁,写锁
    stampedLock是3种模式,写锁,悲观锁,乐观锁

    写锁,悲观锁的语意和ReadWriteLock的写锁,读锁,语意类似,允许多个线程同时获取悲观读锁,但是只允许一个线程获取写锁,写锁和悲观读锁是互斥的
    不同之处StampedLock的写锁和悲观读锁加锁成功后都会返回一个stamp,然后解锁的时候需要传入stamp

    
    final StampedLock sl = 
      new StampedLock();
      
    // 获取/释放悲观读锁示意代码
    long stamp = sl.readLock();
    try {
      //省略业务相关代码
    } finally {
      sl.unlockRead(stamp);
    }
    
    // 获取/释放写锁示意代码
    long stamp = sl.writeLock();
    try {
      //省略业务相关代码
    } finally {
      sl.unlockWrite(stamp);
    }
    

    性能好的的关键是stampedLock支持乐观读方式,ReadWriteLock支持多个线程同时读
    多个线程同时读的时候,所有的写操作会被阻塞;
    stampedLock提供的乐观读,是允许一个线程获取写锁的,不是所有的写操作都会被阻塞
    乐观读而不是乐观读锁,所以性能要更好一些

    
    class Point {
      private int x, y;
      final StampedLock sl = 
        new StampedLock();
      //计算到原点的距离  
      int distanceFromOrigin() {
        // 乐观读
        long stamp = 
          sl.tryOptimisticRead();
        // 读入局部变量,
        // 读的过程数据可能被修改
        int curX = x, curY = y;
        //判断执行读操作期间,
        //是否存在写操作,如果存在,
        //则sl.validate返回false
        if (!sl.validate(stamp)){
          // 升级为悲观读锁
          stamp = sl.readLock();
          try {
            curX = x;
            curY = y;
          } finally {
            //释放悲观读锁
            sl.unlockRead(stamp);
          }
        }
        return Math.sqrt(
          curX * curX + curY * curY);
      }
    }
    

    乐观读期间,存在写操作,会把乐观读升级为悲观读锁

    3.理解乐观读

    数据库的乐观锁场景
    version字段,每次更新product_doc表,都将version字段加1,展示的时候,version字段和其他业务字段一起返回给生产订单ui

    
    select id,... ,version
    from product_doc
    where id=777
    

    用户保存的时候,利用sql 更新订单,假设该生产订单version=9

    
    update product_doc 
    set version=version+1,...
    where id=777 and version=9
    

    如果执行成功,并返回修改条数为1,那么说明从生产订单ui到执行操作,保存数据期间,没有其他人修改过这条数据,因为如果其他人修改过这条数据,那么版本一定大于9

    4.数据库的乐观锁

    查询的时候把version字段查出来,更新的时候利用version字段做验证。
    这个version字段类似于stampedLock中的stamp

    5.stampedLock使用注意事项

    stampedLock没有添加Reentrant前缀,不可重入的锁
    stampedLock的悲观读锁,写锁不支持条件变量
    坑:如果线程阻塞在stampedLock的readLock或者writeLock时,调用interrupt方法时,会导致cpu飙升.使用stampedLock的时候一定不要使用interrupt

    
    final StampedLock lock
      = new StampedLock();
    Thread T1 = new Thread(()->{
      // 获取写锁
      lock.writeLock();
      // 永远阻塞在此处,不释放写锁
      LockSupport.park();
    });
    T1.start();
    // 保证T1获取写锁
    Thread.sleep(100);
    Thread T2 = new Thread(()->
      //阻塞在悲观读锁
      lock.readLock()
    );
    T2.start();
    // 保证T2阻塞在读锁
    Thread.sleep(100);
    //中断线程T2
    //会导致线程T2所在CPU飙升
    T2.interrupt();
    T2.join();
    

    6.StampedLock的官方实例

    
    final StampedLock sl = 
      new StampedLock();
    
    // 乐观读
    long stamp = 
      sl.tryOptimisticRead();
    // 读入方法局部变量
    ......
    // 校验stamp
    if (!sl.validate(stamp)){
      // 升级为悲观读锁
      stamp = sl.readLock();
      try {
        // 读入方法局部变量
        .....
      } finally {
        //释放悲观读锁
        sl.unlockRead(stamp);
      }
    }
    //使用方法局部变量执行业务操作
    ......
    
    原创:做时间的朋友
  • 相关阅读:
    elk
    js时间处理
    idea首次提交项目
    kafka集群zookeeper集群详细配置
    单节点多节点等等详细解释
    kafka原理存储
    Thread-0" kafka.common.FailedToSendMessageException: Failed to send messages after 3 tries.
    如何使用JMeter开源性能测试工具来构建Web性能测试体系
    自动化测试的理解
    VBS教程
  • 原文地址:https://www.cnblogs.com/PythonOrg/p/14977481.html
Copyright © 2011-2022 走看看