ReentrantReadWriteLock 读写锁学习

ReentrantReadWriteLock读写锁学习–未完

JDK1.8读写锁包含了两个锁,读锁readerLock,写锁writerLock

读锁和写锁的的lock()都使用了同一个同步器Sync,唯一区别的是一个是共享锁一个是排它锁

//读锁
public void lock() {
    sync.acquireShared(1);
}
//写锁
public void lock() {
    sync.acquire(1);
}

1. 读锁


public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}

if条件尝试获取读锁,调用tryAcquireShared(int unused)

//Sync
protected final int tryAcquireShared(int unused) {
    Thread current = Thread.currentThread();
    //获取当前锁的状态
    int c = getState();
    //exclusiveCount(c) 返回独占锁(写锁)持有锁的数量
    //exclusiveCount(c)!=0 存在写锁而且持有锁的线程不是当前线程,返回-1
    //进入doAcquireShared(arg),等会看
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
    //获取共享锁(读锁)持有的数量
    int r = sharedCount(c);
    /*
    	!readerShouldBlock() 读锁是否应该被堵塞,公平锁判断是否有前驱
    	非公平锁判断是否
     	r < MAX_COUNT共享线程数小于最大共享数
    	compareAndSetState(c, c + SHARED_UNIT) 尝试改变读锁的数量
    */
    if (!readerShouldBlock() &&
        r < MAX_COUNT &&
        compareAndSetState(c, c + SHARED_UNIT)) {
        //如果之前没有人使用过锁(空闲),将当前线程设置为第一个读线程,读锁+1,直接上锁完事
        if (r == 0) {
            firstReader = current;
            firstReaderHoldCount = 1;
        } else if (firstReader == current) {
            //如果队列空闲,直接读锁+1(重入)
            firstReaderHoldCount++;
        } else {
            //如果当前线程不是队列的第一个
            //The hold count of the last thread to successfully acquire readLock
            //取出最后一个成功获取读锁的线程
            HoldCounter rh = cachedHoldCounter;
            //如果cache没有或者最后获取读锁的不是当前线程,则取出当前线程的累计数
            //设置当前线程为最后一个成功获取读锁的线程
            if (rh == null || rh.tid != getThreadId(current))
                cachedHoldCounter = rh = readHolds.get();
            //如果rh != null 且 累计数为0,将rh设置到ThreadLocal,
            else if (rh.count == 0)
                readHolds.set(rh);
            //累计次数+1
            rh.count++;
        }
        return 1;
    }
    //如果进行到这一步,条件可能是:
    // 1.锁被其他线程占用
    // 2.读线程累计数达到上限
    return fullTryAcquireShared(current);
}

//Fair
final boolean readerShouldBlock() {
    return hasQueuedPredecessors();
}
public final boolean hasQueuedPredecessors() {
    Node t = tail; 
    Node h = head;
    Node s;
    //h != t,head!=tail,队列非空
    // (s = h.next) == null 头的下一个节点为空,这情况应该不可能,既然队列非空了
    // head肯定指向像一个节点,貌似就是一个赋值的过程(个人理解),jdk这帮人有点懒....
    //  s.thread != Thread.currentThread()说明在当前线程之前还有人等待获取锁
    // 整个方法就是判断CLH队列有没有前驱节点在等待获取锁
    return h != t &&
        ( (s = h.next) == null || s.thread != Thread.currentThread());
}

//Nonfair
final boolean readerShouldBlock() {
    return apparentlyFirstQueuedIsExclusive();
}
final boolean apparentlyFirstQueuedIsExclusive() {
    Node h, s;
    //(h = head) != null 说明CLH已经初始化,顺便赋值给h
    //(s = h.next)  != null 说明队列非空,顺便赋值s
    // !s.isShared() 判断下一个获取锁的线程是不是写线程
    return (h = head) != null &&
        (s = h.next)  != null &&
        !s.isShared()         &&
        s.thread != null;
}
final boolean isShared() {
    return nextWaiter == SHARED;
}

//非公平的情况下,判断了CLH下一个要获得锁的是不是写线程,如果是,那么读线程被堵塞,
//进入fullTryAcquireShared
final int fullTryAcquireShared(Thread current) {
   /*
    * This code is in part redundant with that in
	* tryAcquireShared but is simpler overall by not
	* complicating tryAcquireShared with interactions between
	* retries and lazily reading hold counts.
	*/
	//这段代码在一定程度上和tryAcquireShared冗余,但是总体来说是简化了
    //而不是使得tryAcquireShared在重试和延迟读取持有锁数量的过程复杂化
    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 {
                //第一次循环,rh=null,赋值一下
                if (rh == null) {
                    //将最后获取到读锁的线程的计数器赋值给rh
                    rh = cachedHoldCounter;
                    //如果rh还是为空(),或者最后获取到读锁的线程不是当前线程
                    if (rh == null || rh.tid != getThreadId(current)) {
                        //取出自己的持有锁计数器
                        rh = readHolds.get();
                        //如果自己的读锁计数器的计数为0
                        //将自己的读锁计数器移除
                        if (rh.count == 0)
                            readHolds.remove();
                    }
                }
                if (rh.count == 0)
                    return -1;
            }
        }
        //如果读锁累计数达到最大了,抛异常
        if (sharedCount(c) == MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        //尝试设置读锁状态+1
        if (compareAndSetState(c, c + SHARED_UNIT)) {
            //如果锁是空闲的
            if (sharedCount(c) == 0) {
                //设置当前线程为读锁的第一个线程
                firstReader = current;
                //设置读锁第一个读线程计数器计数设为1
                firstReaderHoldCount = 1;
            } else if (firstReader == current) {
                //如果当前线程就是读锁中第一个读线程,则当前线程获取读锁累计数+1
                firstReaderHoldCount++;
            } else {
                //如果当前线程不是读锁的第一个线程,且读锁不是空闲的
                
                //第一次循环,rh=null的,赋值一下
                if (rh == null)
                    rh = cachedHoldCounter;
                //如果赋值之后,还是空的或者最后获取到读锁的线程并不是当前线程
                if (rh == null || rh.tid != getThreadId(current))
                    //将自己的持有锁计数器取出来
                    rh = readHolds.get();
                else if (rh.count == 0)
                    //如果自己持有锁计数器的累计数为0,初始化一下
                    readHolds.set(rh);
                //最后再将自己持有读锁的计数器+1
                rh.count++;
                //将最后成功获取到读锁的计数器设置为当前
                cachedHoldCounter = rh; // cache for release
            }
            return 1;
        }
    }
}
//fullTryAcquireShared 方法整体就是循环cas,将读锁状态设为+1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值