heima并发26---并发工具包(5)---读写锁原理和java8的stmaplock--253--260

读写锁的流程。

读写锁用的是一个aqs的同步器。

我们的流程是t1加写锁,t2加读锁。

state既要给写锁也要给读锁用。

 

写锁占了低16位,读锁占了高16位。

state 0没有加锁1加了锁 大于1多次重入,也可能是不同的读锁加锁的。

看下源码:

写锁的lock。

和之前一样,尝试加锁失败了才会进入队列的。

我们稍微看下tryAcquire

state不为0表示即可能是加了读锁,也可能是加了写锁。

为0进入如下的方法,加锁。

这个方法是判断公平还是非公平的,如果为非公平锁一直返回的是false。这个就是检查老二的,非公平事不检查老二的。

公平锁检查老二,看下有没有老二。

这个方法,如果是非公平锁总会返回的是false。

要是公平锁要检查队列看下队列的老二是不是有了,有了你要排在后面的。

要是非公平锁就是改state,设置为owner线程。

加锁成功就不在等待队列里面了。

---

c不为0,可能加的是读锁,也可能加的是写锁。

要是加的为写锁的话:

w==0加的是读锁,和我们要加的写锁是互斥的所以return为false。

w!=0不是当前的线程也是return的false。

上面这个流程是t1加的写锁。

写锁的获取流程:https://blog.csdn.net/fygu18/article/details/81784574

其实加锁成功就两步设置status和设置独占线程。

---253---

t1线程的写锁加成功了,接着就是t2加读锁了。

读写锁就是失败返回-1成功返回1。大于1是信号量的。

找读锁的lock。

 

返回值的说明:

区别:

-1表示失败,但是0和正数表示成功,0表示没有后继节点需要唤醒,整数表示还有几个后继节点需要唤醒。

读写锁就是0和1,信号量是大于1的。

进去看代码:

这个就是检查写锁的,看下有没有加写锁。

看下加写锁的是不是自己。这个意思是,同一个线程已经加了写锁,再加读锁的话是可以成功的。

这里t1已经加了写锁,t2加读锁,直接返回的是-1。

----

读锁的最终到这里:

我们只分析的是-1的情况就是

t2线程要进队列里面的。

看下加的节点类型:

加入的节点是shared类型的节点,这个要注意。执行完addWaiter之后还是活跃的状态的。

找下t2有没有前驱节点,节点是head说名是老二,有资格获得锁,再次调用tryAcquired

处于活跃状态就是没有被park住。

进入下面这个if,就是失败了是不是该阻塞住。

第一次是前驱节点设置为-1,返回false,第二次通过判断返回true。

内部将前驱节点设置为-1。再进入循环。

这个文章很好:https://blog.csdn.net/chen77716/article/details/6641477

最后park住:

---254---

t1加了写锁,t2加了读锁。

再来t3加读锁,t4加写锁,此时变成这个样子。

因为没有释放写锁,所以t3线程和t4线程都会加入到里面的。

两点注意:

1.0 -1 -1是有职责去唤醒后面的节点,是waitStatus属性。

2.独占和共享状态,就是node的shared属性和ex属性。

---

此时t1是写锁,t2 t3是读锁 t4是写锁。

---255---

t1忙完了,写锁的unlock。

我们看下代码:

我们看下tryRelease:

首先减去1,查看是不是减为0,查看要查看写锁的部分是不是减为0的。

当前执行的线程设置为null。

回来主方法,再来,这时候假设写锁都解开了。

执行唤醒流程,唤醒等待队列头结点的下一个节点。

我们想下t2读锁是在哪里被暂停的呢?

共享的读锁我们找到,这个方法相当于reentrantLock的acquiredQueue。

在这里继续运行。

前驱节点是head才有资格去竞争锁的。

进入tryAcquireShared方法看怎么竞争成功的。

此时这个为0,独占锁不为0肯定不能竞争成功的。

这里分析过了,不分析了往下分析。

这个方法以后再说,这个就是公平非公平。

c的写锁为0,则读锁基数+1,是高位+65536。

直接进入这个方法:

读锁的高位+1。下面的不看了。

return 1。读锁加锁成功。

再来就是这个方法,加锁成功再往后走。

加锁成功但是不是独占锁,所以是不能放在这里的。

进入:断开头节点

传入的是当前的节点和1

当前节点的下一个节点还是共享节点的话进入这个方法。

这里把头节点的状态由-1改为0。

把头节点去掉,t2所在的节点改为头节点。t2是当前线程。

---256---

还没完呢?

进入到sethead方法:

这个方法看下。

首先拿到当前节点的下一个节点。

就是这个:

就是判断t3是不是shared。

是的话调用这个方法:

头节点的状态尝试由-1改为0,防止改动的时候其他的线程受到干扰,-1是我要唤醒后继节点的。失败会continue。

再来:头节点的后续节点被唤醒。

这个就是唤醒的t3。

唤醒就是结束阻塞然后再循环。就是再进入到doAcquirieShared方法。

--

t3也是唤醒共享锁时候阻塞住的。

进入tryAcquireShared:

 

此时这个是1再+1。

共享读锁多个线程都可以让基数增加。

看下这个方法:

读的话就是一个唤醒,其他的都唤醒的,大家都读,因为不是互斥的

流程赏析:

最后是这样的了:

---257---

读锁的unlock:

现在t2和t3都是运行的状态,但是还没有unlock呢。

进入读锁的unlock。

第一步:

进入tryReleaseShared

可知,拿到state状态。

此时在这里减去1.

不是0则返回的是false。

此时不会进入到doReleaseShared的。

再次进入:

读锁是不用知道当前的线程的节点的,就是没有

t2 t3都进去了就减为0了。

进入:

改是首先尝试把头节点的状态由-1改为0,防止干扰。

开始唤醒。

t4在哪里恢复运行呢?在这里

在这里恢复与运行的。

注意设置这个在哪里:

---258---

StampedLock:

乐观读:戳失败才升级为读锁。

---259---

代码:

---260---

乐观读的读锁写锁都不支持条件变量的。

ReentrantWeiteReadLock:读锁不支持条件变量,写锁可以降级重入。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值