ReentrantReadWriteLock源码分析与原理介绍

之前看了ReentrantLock独占锁和Semaphore共享锁的原理和介绍,结合这两个锁的就是ReentrantReadWriteLock读写锁。

读写锁就是:读读共享,读写互斥,写读互斥,写写互斥。

读写锁的特性:

1、支持公平与非公平锁的获取

         如: public ReentrantReadWriteLock(boolean fair)//默认是非公平的

2、重进入:如:当一个读线程获取了读锁之后能够再次获取到读锁

3、锁降级:

        锁降级就有必要说说了。锁降级指的是:写锁降级为读锁。指把持住当前拥有的写锁,再获取到读锁,锁喉释放写锁的过程,这时只拿了一个读锁,这个过程就是锁降级

但是:::我们看下代码:

(在图片中我说了下我的疑虑和如何实现锁降级,以及自己的分析)


那么它的内部实现是怎么实现的呢?其实也是对状态state做修改,当阻塞的时候会将被阻塞的没有拿到锁的放到同步队列中。

1、我们先说下它的原理--是如何既能保存写状态也能保存读状态的。

读写锁同样依赖于自定义同步器来实现同步功能,而读写转态就是同步器的同步状态。

我们的同步状态是一个int类型的,我们需要按位分割使用这个变量。读写锁分成两部分:高16位是读,低16位是写

设:当前状态为S

当低16位有值时,说明存在写锁。当写状态+1的时候:S+1  (存在写锁,又加1,说明是当前线程写锁重入)

当高16中不全为0时,说明存在读锁,当读状态+1的时候。高16位加1就是 1<<16,(1是低位即:0000000000000001 ,将1向左移动16位,),那么最终的S值为 S+(1<<16)


下面我们来看下源码:

写锁的获取与释放:


这里我们贴一下addWaiter和acquireQueued的源码




写锁的释放:


-------------------------------------下面我们看下读锁---------------------------------------------------

读锁就是我们之前所说的共享锁,这里只不过我们与写锁共用了一个状态变量。下面看下他的实现


获取锁的逻辑如下:


上图中的:fullTryAcquireShared(current)-->读取的完整版本,用于处理CAS缺失和可重入读取

里面是一个死循环,重新尝试获取锁,包含了锁降级和锁重入的实现。

 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.
             */
            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 != current.getId()) {
                                rh = readHolds.get();
                                if (rh.count == 0)
                                    readHolds.remove();
                            }
                        }
                        if (rh.count == 0)
                            return -1;
                    }
                }
                if (sharedCount(c) == MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                if (compareAndSetState(c, c + SHARED_UNIT)) {
                    if (sharedCount(c) == 0) {
                        firstReader = current;
                        firstReaderHoldCount = 1;
                    } else if (firstReader == current) {
                        firstReaderHoldCount++;
                    } else {
                        if (rh == null)
                            rh = cachedHoldCounter;
                        if (rh == null || rh.tid != current.getId())
                            rh = readHolds.get();
                        else if (rh.count == 0)
                            readHolds.set(rh);
                        rh.count++;
                        cachedHoldCounter = rh; // cache for release
                    }
                    return 1;
                }
            }
        }

 

上图我们在Semaphore共享锁的实现中也说过,代码逻辑是一样的。

这里想说下我昨天刚刚遇到的一个场景,用到的是共享锁,--Semaphore。

场景:某系统有一个通过上传文件同步数据的功能,我们想控制一下,当用户上传少于5个的时候我们就直接让其执行,并显示状态为正在执行中,当上传数大于5个的时候,比如是N,那么N-5的文件,就需要显示状态为等待状态,当有一个上传成功或失败后,就需要从等待队列中取一个,变为正在执行中。这么一个需求;

   当时我就想到了Semaphore来用信号量控制,但是boss非得给我说创建两个队列,一个是等待队列,一个是执行队列,我们还得搞个Map或者List容器存所有的上传任务。来实现云云的。当然按照他说的逻辑也能实现,但是实现起来也是有一定难度的,考虑的东西很多,而且是按用户区别的,就是,当另一个用户操作时,并不受第一个用户的影响,但是也要实现这样的功能,,这样就有套了一层,我想了一天,觉得他那种还是比较麻烦,就用了Semaphore.

原因:其实Semaphore就是控制并发线程数量的,其内部已经实现了等待队列阻塞,而且还是线程安全的,对于上面的问题我们就不要考虑那么多。省了90%的工作量。我们只需要在每个用户调用该功能的时候就为每个用户创建一个Semaphore信号量,可以公平也可以非公平,每次只能获取1个许可,一共5个许可。当该用户再次来获取的时候,我们直接从Map里取一下之前创建的Semaphore信号量就可以了。


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值