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;
    }
}
相关推荐
thePLJ19 分钟前
SpringBoot-已添加并下载的依赖,reload和mvn clean 后还是提示找不到jar包问题
java·spring boot·后端
余华余华22 分钟前
输入输出 数组 冒泡排序举例
java·后端
俞嫦曦33 分钟前
R语言的回归测试
开发语言·后端·golang
JalenYan34 分钟前
Ruby on Rails 中的 Delegated Types(委托类型)
后端·ruby on rails·ruby
hxung37 分钟前
spring bean的生命周期和循环依赖
java·后端·spring
油丶酸萝卜别吃41 分钟前
springBoot中不添加依赖 , 手动生成一个token ,并校验token (使用简单 , 但是安全会低一点)
spring boot·后端·安全
皮皮的江山1 小时前
基于AI Text2SQL的数据可视化方案
后端·aigc·数据可视化
4dm1n1 小时前
kubectl exec 实现的原理
后端
四七伵1 小时前
高性能 MySQL 必备:COUNT(*)、COUNT(1)、COUNT(字段) 的选择法则
后端·mysql
用户86178277365181 小时前
责任链
java·后端·设计模式