ReentrantReadWriteLock源码分析

整体概念

  • ReentrantReadWriteLoc提供读写锁机制,读锁共享,写锁互斥,读写分离
  • 写锁可以降级为读锁
  • 支持公平锁和非公平锁

核心内部类分析

Sync

Sync继承AQS实现锁的逻辑,有关AQS的原理参考juejin.cn/post/748158... ,本文涉及AQS的方法不再重复分析。

HoldCounter

arduino 复制代码
static final class HoldCounter {
    //记录线程的读锁重入次数
    int count = 0;
    // Use id, not reference, to avoid garbage retention
    final long tid = getThreadId(Thread.currentThread());
}

ThreadLocalHoldCounter

ThreadLocal的子类,表示读锁线程的HoldCounter

csharp 复制代码
static final class ThreadLocalHoldCounter
    extends ThreadLocal<HoldCounter> {
    public HoldCounter initialValue() {
        return new HoldCounter();
    }
}

属性字段

arduino 复制代码
//读锁偏移量
static final int SHARED_SHIFT   = 16;
//读锁偏移单位
static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
//最大重入次数
static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
//掩码
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
arduino 复制代码
//获取高16位读锁的数量
static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
//获取低16位写锁的数量
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

构造方法

scss 复制代码
Sync() {
    //初始读锁的线程持有数量
    readHolds = new ThreadLocalHoldCounter();
    //确保readHolds的可见性,防止其他线程获取未初始化完成的readHolds对象
    // volatile的写操作会立即刷新到主内存,并防止指令重排序,同时还会强制其之前的所有普通写操作也刷新到主内存,所以这里也保证了其他线程看到初始化完成的readHolds
    setState(getState()); // ensures visibility of readHolds
}

NonfairSync 非公平锁

writerShouldBlock()

arduino 复制代码
//写操作是否需要阻止
final boolean writerShouldBlock() {
    // 不阻止  允许新来的写锁线程和队列中的线程竞争(允许插队)
    return false; // writers can always barge
}
arduino 复制代码
//读操作是否需要阻止
final boolean readerShouldBlock() {
    //看队列的第一个有效节点是否是写锁线程
    return apparentlyFirstQueuedIsExclusive();
}
java 复制代码
final boolean apparentlyFirstQueuedIsExclusive() {
    Node h, s;
    return 
    //队头不为空(哨兵节点)
    (h = head) != null &&
        //第一个有效节点
        (s = h.next)  != null &&
        //是否是共享模式  
        !s.isShared()         &&
        //是否是有效线程
        s.thread != null;
}

FairSync 公平锁

arduino 复制代码
//写操作是否需要阻止
final boolean writerShouldBlock() {
    //同步队列中有有效节点在排队,公平锁则不插队去枪锁
    return hasQueuedPredecessors();
}
java 复制代码
public final boolean hasQueuedPredecessors() {
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return
    //h==t的话说明队列是空的或者队列仅有一个无效等待的节点
    //h!=t说明同步队列中存在有效节点在排队
    h != t &&
        //说明头节点已有值(end()方法入队时候pre已经设置了值,但tail还没来得及设置,这时候认为有节点在排队)
        ((s = h.next) == null || 
        //不等于当前线程,说明有其他节点在排队
        s.thread != Thread.currentThread());
}
arduino 复制代码
//读操作是否需要阻止
final boolean readerShouldBlock() {
    return hasQueuedPredecessors();
}

构造方法

csharp 复制代码
public ReentrantReadWriteLock() {
    this(false);
}
arduino 复制代码
public ReentrantReadWriteLock(boolean fair) {
    //默认非公平锁
    sync = fair ? new FairSync() : new NonfairSync();
    //构造读锁  非公平
    readerLock = new ReadLock(this);
    //构造写锁  非公平
    writerLock = new WriteLock(this);
}

加锁

写锁加锁

csharp 复制代码
public void lock() {
    sync.acquire(1);
}
scss 复制代码
public final void acquire(int arg) {
    if (
    //尝试获取锁
    !tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
java 复制代码
protected final boolean tryAcquire(int acquires) {
    //当前线程
    Thread current = Thread.currentThread();
    //状态值
    int c = getState();
    //写锁数量
    int w = exclusiveCount(c);
    //说明有读锁或者是写锁
    if (c != 0) {
        //这段源码注释的意思是存在读锁,那么写锁加锁直接失败,因为读锁和写锁是互斥的
        // (Note: if c != 0 and w == 0 then shared count != 0)
        if (
        //写锁的数量是0,加上c!=0(存在读锁或者是写锁)的条件,即从c!= && w==0 说明是读锁持有,那么写锁加锁肯定失败的,因为读锁和写锁是互斥的
        w == 0 || 
        //不存在读锁的话(写锁状态),不是当前线程独占,说明有别的写锁线程还在临界区执行代码没有释放锁,加锁失败,写锁和写锁也是互斥的
        current != getExclusiveOwnerThread())
            return false;
            //超过最大数量抛异常
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        // Reentrant acquire
        //到这里说明是写锁自己加锁,没有别的写锁和读锁干扰,设置state即可
        setState(c + acquires);
        //写锁加锁成功
        return true;
    }
    //是否需要阻止加锁
    //这里是非公平锁,允许和同步队列中的线程枪锁
    if (writerShouldBlock() ||
        //CASstate加1
        !compareAndSetState(c, c + acquires
        //加锁失败
        return false;
    //成功则设置独占标识    
    setExclusiveOwnerThread(current);
    return true;
}

读锁加锁

csharp 复制代码
public void lock() {
    sync.acquireShared(1);
}
arduino 复制代码
public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        //加锁失败加入同步队列
        doAcquireShared(arg);
}
ini 复制代码
protected final int tryAcquireShared(int unused) {
    Thread current = Thread.currentThread();
    int c = getState();
    if (
    //写锁的数量不为0,说明有写锁加锁过还没有释放完
    exclusiveCount(c) != 0 &&
        //不是当前线程,说明也不是当前线程持有的写锁,若是当前线程持有的写锁是允许读锁加锁的(锁的降级)
        getExclusiveOwnerThread() != current)
        return -1;
    //读锁数量    
    int r = sharedCount(c);
  
    if (
    //读锁是否需要被阻止
    //队列的第一个有效节点是写锁,读锁不去竞争锁
    //防止读锁一直抢到锁导致写锁得不到释放造成写锁饥饿
    !readerShouldBlock() &&
        
        r < MAX_COUNT &&
        //CAS读锁数量
        compareAndSetState(c, c + SHARED_UNIT)) {
        //读锁数量为0
        if (r == 0) {
            //当前线程第一个获取读锁
            firstReader = current;
            //持有次所为1
            firstReaderHoldCount = 1;
            
        } 
        //第一个获取读锁的线程再次加锁
        else if (firstReader == current) {
            //持有次数加1
            firstReaderHoldCount++;
        } else {
            //最后一次读锁线程持有数量的缓存
            HoldCounter rh = cachedHoldCounter;
            //为空或者不是当前线程
            if (rh == null || rh.tid != getThreadId(current))
                //更新缓存
                cachedHoldCounter = rh = readHolds.get();
            //计数为0,设置缓存    
            else if (rh.count == 0)
                readHolds.set(rh);
            //当前线程计数加1    
            rh.count++;
        }
        return 1;
    }
    //循环加锁
    return fullTryAcquireShared(current);
}
csharp 复制代码
final int fullTryAcquireShared(Thread current) {

    HoldCounter rh = null;
    for (;;) {
        int c = getState();
        //写锁存在
        if (exclusiveCount(c) != 0) {
            //不是当前线程持有的写锁,读锁和写锁互斥加锁失败
            if (getExclusiveOwnerThread() != current)
                return -1;
            // else we hold the exclusive lock; blocking here
            // would cause deadlock.
        } else if (
         //说明没有写锁持有锁,读锁可以尝试加锁
        //读锁操作是否要阻止
        readerShouldBlock()) {
            // Make sure we're not acquiring read lock reentrantly
            //确保没有重入获取读锁
            if (firstReader == current) {
                // assert firstReaderHoldCount > 0;
            } else {
                if (rh == null) {
                    rh = cachedHoldCounter;
                    if (rh == null || 
                    //不是当前线程
                    rh.tid != getThreadId(current)) {
                        //设置当前线程readHolds
                        rh = readHolds.get();
                        //当前线程数锁记录为0
                        if (rh.count == 0)
                            //清除无效的锁记录
                            readHolds.remove();
                    }
                }
                //不是读锁重入,状态为0加锁失败
                if (rh.count == 0)
                    return -1;
            }
        }
        //校验读锁的数量
        if (sharedCount(c) == MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        //设置读锁状态    
        if (compareAndSetState(c, c + SHARED_UNIT)) {
            //读锁数量是0
            if (sharedCount(c) == 0) {
                //设置firstReader
                firstReader = current;
                //设置firstReaderHoldCount
                firstReaderHoldCount = 1;
            } 
            //读锁重入
            else if (firstReader == current) {
                //计数加1
                firstReaderHoldCount++;
            } else {
              
                if (rh == null)
                    //为空更新
                    rh = cachedHoldCounter;
                    //为空或者不是当前线程
                if (rh == null || rh.tid != getThreadId(current))
                    //从ThreadLocal中更新
                    rh = readHolds.get();
                //计数为0
                else if (rh.count == 0)
                    //重新设置
                    readHolds.set(rh);
                //计数加1    
                rh.count++;
                //更新缓存
                cachedHoldCounter = rh; // cache for release
            }
            //加锁成功
            return 1;
        }
    }
}

解锁

写锁解锁

csharp 复制代码
public void unlock() {
    sync.release(1);
}
java 复制代码
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            //唤醒后继线程
            unparkSuccessor(h);
        return true;
    }
    return false;
}
scss 复制代码
protected final boolean tryRelease(int releases) {
    //不是独占报错
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    //状态减1    
    int nextc = getState() - releases;
    //卸货状态是否为0
    boolean free = exclusiveCount(nextc) == 0;
    //是的话
    if (free)
        //清空独占线程标识
        setExclusiveOwnerThread(null);
    //设置状态值    
    setState(nextc);
    return free;
}

读锁解锁

csharp 复制代码
public void unlock() {
    sync.releaseShared(1);
}
arduino 复制代码
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        //解锁成功唤醒后继所有节点
        doReleaseShared();
        return true;
    }
    return false;
}
scss 复制代码
protected final boolean tryReleaseShared(int unused) {
    Thread current = Thread.currentThread();
    //当前线程是firstReader
    if (firstReader == current) {
        // assert firstReaderHoldCount > 0;
        if (firstReaderHoldCount == 1)
            firstReader = null;
        else
            firstReaderHoldCount--;
    } else {
        //获取缓存
        HoldCounter rh = cachedHoldCounter;
        //缓存为空或者是当前线程
        if (rh == null || rh.tid != getThreadId(current))
            //获取值
            rh = readHolds.get();
         //读锁加锁次数   
        int count = rh.count;
        //小于等于1
        if (count <= 1) {
            //清除
            readHolds.remove();
            //校验重复释放
            if (count <= 0)
                throw unmatchedUnlockException();
        }
        //加锁次数减1
        --rh.count;
    }
    for (;;) {
        //状态值
        int c = getState();
        //读锁值
        int nextc = c - SHARED_UNIT;
        //CAS
        if (compareAndSetState(c, nextc))
            // Releasing the read lock has no effect on readers,
            // but it may allow waiting writers to proceed if
            // both read and write locks are now free.
            //高16位为0说明读锁释放了
            return nextc == 0;
    }
}
相关推荐
一勺菠萝丶1 小时前
深入浅出:Spring Boot 中 RestTemplate 的完整使用指南
java·spring boot·后端
海风极客2 小时前
《Go小技巧&易错点100例》第三十二篇
后端·spring·golang
有梦想的攻城狮2 小时前
SpEL(Spring Expression Language)使用详解
java·后端·spring·spel
caihuayuan52 小时前
前端面试2
java·大数据·spring boot·后端·课程设计
郭尘帅6663 小时前
SpringBoot学习(上) , SpringBoot项目的创建(IDEA2024版本)
spring boot·后端·学习
野犬寒鸦3 小时前
MySQL索引详解(下)(SQL性能分析,索引使用)
数据库·后端·sql·mysql
.生产的驴7 小时前
SpringBoot 集成滑块验证码AJ-Captcha行为验证码 Redis分布式 接口限流 防爬虫
java·spring boot·redis·分布式·后端·爬虫·tomcat
野犬寒鸦9 小时前
MySQL索引使用规则详解:从设计到优化的完整指南
java·数据库·后端·sql·mysql
思考的橙子9 小时前
Springboot之会话技术
java·spring boot·后端
兆。12 小时前
电子商城后台管理平台-Flask Vue项目开发
前端·vue.js·后端·python·flask