zoukankan      html  css  js  c++  java
  • ReentrantReadWriteLock 读写锁

      ReentrantLock 中还有 ReentrantReadWriteLock 这么一个变种,读读不互斥,读写互斥,写写互斥。

    写锁

      一开始,就是 tryAcquire 获取锁,如果获取失败就返回 false ,那就走 addWaiter 将请求入队,然后 acquireQueued 挂起线程。和 ReentrantLock 一毛一样,那我们就来分析分析它的这个 加锁逻辑有何不同吧。

    第一次线程过来
    1. 首先获取到当前线程,state (默认肯定是为0的),state 的低16位 w(state 低16位标识写锁,高16位是标识读锁的 )
    2. c != 0 那就说明被人加过锁了,我们现在是第一次进来,那么肯定不走这里,先跳过。
    3. writerShouldBlock() 没任何逻辑,直接返回false了
    4. 然后就是 cas 加锁,成功了就返回false,标记线程锁占用标识。

    第 N 次线程过来
    1. 首先获取到当前线程,state (假设被人加锁了,那么就是1),w(state 的低16位,写锁标识 )
    2. c != 0 那就说明被人加过锁了,进来这个分支。
    3. w == 0 ,说明之前加的是读锁,或者 不是当前线程,那就加锁失败返回false
    4. 能走到下面的 if ,就说明 之前是当前线程家的写锁,那么 state+1就完了。
    5. 最后走到 setExclusiveOwnerThread 设置线程占据锁标识

    所以说 写锁 和 读/写锁 都是互斥的压根进不来。所谓的读写锁就是靠 state 的高低16位来搞的。

    释放锁就没啥说的了,state-1,占用锁线程置空,然后唤醒头部节点。

    读锁

    首先上来就是 tryAcquireShared() 尝试获取锁,他有三种情况

    (1) 被人加过写锁
    1. 获取到当前线程 和 加锁状态 c
    2. exclusiveCount(c) 判断低16位是否 != 0 ,如果不等于0,就说明加过写锁了
    3. 如果 != 0 ,那么后面再看 当前线程是否加锁线程,不是当前线程就直接返回加锁失败

    (2) 没被人加过锁,或者加过读锁,或者自己加过写锁
    1. 获取 state 的高16位 c (读锁标识)
    2. readerShouldBlock() 判断是否需要阻塞,默认非公平锁肯定不用的,(里面判断依据是 !s.isShared() 如果线程不是加读锁,我们现在加的就是读锁,那么肯定是false)
    3. 开始 cas 加锁
    3.1 如果 r == 0,说明没被人加过锁、自己是第一个。
    3.2 如果 firstReader == current ,说明自己加过锁,现在自己重入加锁
    3.3 如果以上都不是,说明别人加过读锁,那么现在在 HoldCounter 记录最后一个加锁线程信息(也就是自己)

    (3) r < MAX_COUNT 不成立的时候,没有加锁
    1. 此时它会执行 fullTryAcquireShared() 再次尝试获取锁,整体代码其实和上面逻辑没啥区别,就不读了。

      假如上面没有获取到锁,return -1 的话,那就执行阻塞等待的逻辑了,如果节点 p 是头节点,那就说明当前线程是队列中的第一个节点,他就尝试获取锁,如果获取成功就 p.next=null 将自己从队列中摘除掉;反之,打断当前线程,把自己挂起来。

     

     。

  • 相关阅读:
    委托~~~~~~~~~~~~~
    Lambda表达式的前世今生~~~~~~
    数据库/MySQL的安装
    flask 源码专题(十一):LocalStack和Local对象实现栈的管理
    python面试题:redis数据库
    python面试题七: mysql数据库
    wtforms: remove ' fill out this field'
    python面试题六: 剑指offer
    python面试题五:Python 编程
    python面试题四:Python web框架
  • 原文地址:https://www.cnblogs.com/wlwl/p/15032575.html
Copyright © 2011-2022 走看看