读写锁--ReentrantReadWriteLock
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();monitor
int c = getState(); // 重入锁的计数
int w = exclusiveCount(c); // 计数高低位拆开为读计数跟写计数,计算写计数
if (c != 0) { // 有人在占有锁
if (w == 0 || current != getExclusiveOwnerThread()) // 写计数为0,只有读锁直接返回(避免了读锁升级为写锁) 或者 当前线程不是执行线程(执行线程可能读也可能写)也返回
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT) //写锁重入次数 > 65525,抛出异常
throw new Error("Maximum lock count exceeded");
setState(c + acquires); //重入的写线程,直接设置状态(第6行代码没有return,说明当前线程是重入的写线程(写计数不是0,且current就是获取锁的线程))
return true;
}
if (writerShouldBlock() || !compareAndSetState(c, c + acquires)) //c!=0没有return,说明当前锁是空着的,所以cas抢占
return false;
setExclusiveOwnerThread(current); // 当前线程参数设置
return true;
}
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //没有获取到,就往等待队列添加节点,然后挂起线程
selfInterrupt();
}
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively()) //没有写锁,抛异常
throw new IllegalMonitorStateException();
int nextc = getState() - releases;
boolean free = exclusiveCount(nextc) == 0; //次数是否清0了
if (free) //清0 了,说明完全释放了
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current) // 有写锁 且当前线程不是写锁线程,不能重入,失败
return -1;
int r = sharedCount(c); //向右移位,获取读锁计数
if (!readerShouldBlock() && r < MAX_COUNT && compareAndSetState(c, c + SHARED_UNIT)) { // 若无需排队 && 读计数<65535(16位最大值) && 状态设置成功(读锁的整体计数就是在这里改的,注意加了一个默认值的操作)
if (r == 0) { //读锁为0
firstReader = current; //记录第一个获取锁的线程
firstReaderHoldCount = 1;
} else if (firstReader == current) { //是自己重入的
firstReaderHoldCount++;
} else { //
HoldCounter rh = cachedHoldCounter; //用于计算读计数的计数器
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++; //当前线程的读计数+1
}
return 1;
}
return fullTryAcquireShared(current); //第7行条件不满足,则for循环获取读锁(实际不会死循环的)
}
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) {
int c = getState();
if (exclusiveCount(c) != 0) { // 有写锁
if (getExclusiveOwnerThread() != current) // 写锁持有者不是当前线程,获取失败,通过aqs的doAcquireShared()进入排队
return -1; //这里只做了不是当前线程的判断,如果是当前线程,这个地方不能进行排队,因为若已有写线程在排队的话,就会造成死锁,源码中else一句的英文备注就是说这个
} else if (readerShouldBlock()) { //没写锁,但可能有写锁在等待读锁释放!!需要排队
// 写锁空闲 且 公平策略决定 线程应当被阻塞
// 下面的处理是说,如果是已获取读锁的线程重入读锁时, 即使公平策略指示应当阻塞也不会阻塞。
// 否则,这也会导致死锁的。
if (firstReader == current) { //
// assert firstReaderHoldCount > 0;
} else {
// threadlocal 相关处理
if (rh.count == 0) // 需要阻塞且是非重入(还未获取读锁的),获取失败
return -1;
}
}
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) { // cas 抢锁成功
//threadlocal相关处理
return 1;
}
}
}
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {// 清理firstReader缓存 或 readHolds里的重入计数
// 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;
if (count <= 1) { //完全释放读锁
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count; // 主要用于重入退出
}
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
// 释放读锁对其他读线程没有任何影响,
// 但可以允许等待的写线程继续,如果读锁、写锁都空闲。
return nextc == 0;
}
}
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
public void processCachedData() {
readLock.lock();
if(!update){
//必须先释放读锁
readLock.unlock();
//锁降级从写锁获取到开始
writeLock.lock();
try{
if(!update){
//准备数据的流程(略)
update = true;
}
readLock.lock();
}finally {
writeLock.unlock();
}
//锁降级完成,写锁降级为读锁
}
try{
//使用数据的流程
}finally {
readLock.unlock();
}
}
读写锁--ReentrantReadWriteLock的更多相关文章
- java 可重入读写锁 ReentrantReadWriteLock 详解
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt206 读写锁 ReadWriteLock读写锁维护了一对相关的锁,一个用于只 ...
- [图解Java]读写锁ReentrantReadWriteLock
图解ReentrantReadWriteLock 如果之前使用过读写锁, 那么可以直接看本篇文章. 如果之前未使用过, 那么请配合我的另一篇文章一起看:[源码分析]读写锁ReentrantReadWr ...
- 读写锁ReentrantReadWriteLock:读读共享,读写互斥,写写互斥
介绍 DK1.5之后,提供了读写锁ReentrantReadWriteLock,读写锁维护了一对锁:一个读锁,一个写锁.通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升.在读多写少的情况下, ...
- Java并发(十):读写锁ReentrantReadWriteLock
先做总结: 1.为什么用读写锁 ReentrantReadWriteLock? 重入锁ReentrantLock是排他锁,在同一时刻仅有一个线程可以进行访问,但是在大多数场景下,大部分时间都是提供读服 ...
- 轻松掌握java读写锁(ReentrantReadWriteLock)的实现原理
转载:https://blog.csdn.net/yanyan19880509/article/details/52435135 前言 前面介绍了java中排它锁,共享锁的底层实现机制,本篇再进一步, ...
- Java并发指南10:Java 读写锁 ReentrantReadWriteLock 源码分析
Java 读写锁 ReentrantReadWriteLock 源码分析 转自:https://www.javadoop.com/post/reentrant-read-write-lock#toc5 ...
- 线程高级篇-读写锁ReentrantReadWriteLock
转载原文:http://blog.csdn.net/john8169/article/details/53228016 读写锁: 分为读锁和写锁,多个读锁不互斥,读锁和写锁互斥,这是有JVM自己控制的 ...
- java并发之读写锁ReentrantReadWriteLock的使用
Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象.两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象. 读写锁:分为读 ...
- [源码分析]读写锁ReentrantReadWriteLock
一.简介 读写锁. 读锁之间是共享的. 写锁是独占的. 首先声明一点: 我在分析源码的时候, 把jdk源码复制出来进行中文的注释, 有时还进行编译调试什么的, 为了避免和jdk原生的类混淆, 我在类前 ...
- 读写锁ReentrantReadWriteLock的使用
package com.thread.test.Lock; import java.util.Random; import java.util.concurrent.locks.Lock; impor ...
随机推荐
- ExecutorService的submit方法的坑
先看一段代码: public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerExcept ...
- sudo -s/sodo -i/su root
sudo : 暂时切换到超级用户模式以执行超级用户权限,提示输入密码时该密码为当前用户的密码,而不是超级账户的密码.不过有时间限制,Ubuntu默认为一次时长15分钟.su : 切换到某某用户模式,提 ...
- 《JavaScript权威指南》(第6版)翻译错误集 更新中。。。
§6.2.2 P126 原文:If o inherits x,and that property is an accessor property with a setter method. 原译:如 ...
- WPF 之 UI 异步交互
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(()=> { //··················· ...
- Java 访问修饰符使用规则
作用域 当前类 同一package 子孙类 其他package public √ √ √ √ protected √ √ √ × friendly √ √ × × private √ × × × 1. ...
- HAOI2014 遥感监测
题目链接:戳我 比较水的一个题,直接处理点,找在直线上的可以覆盖到它的区间,然后做最小线段覆盖即可: 代码如下: #include<iostream> #include<cstdio ...
- 为什么要使用rem
为什么要使用rem 今天2019年4月16号更新,模仿网易移动端的的写法: html { font-size: 13.33333vw } @media screen and (max-width:32 ...
- kali linux之Meterpreter
高级,动态,可扩展的payload 基于meterpreter上下文利用更多漏洞发起攻击,后渗透阶段一站式操作界面 完全基于内存的dll注入式payload(不写硬盘) 注入合法系统进程并建立stag ...
- leecode刷题(12)-- 整数反转
leecode刷题(12)-- 整数反转 整数反转 描述: 给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转. 示例 1: 输入: 123 输出: 321 示例 2: 输入: - ...
- 驼峰转大写(javaScript)
var a = function(s){return s.replace(/([A-Z])/g,"_$1").toUpperCase();} F12控制台可以直接用