java源码 - ReentrantReadWriteLock写锁介绍
开篇
这篇文章主要从源码角度讲解ReentrantReadWriteLock的WriteLock的加锁和减锁过程。
ReentrantReadWriteLock的WriteLock加锁解锁过程依赖于AbstractQueuedSynchronizer(AQS)类,所以有些相同的逻辑可以看看ReentrantLock的逻辑。
- ReentrantReadWriteLock的数据结构介绍
- java源码 - ReentrantReadWriteLock读锁介绍
- java源码 - ReentrantReadWriteLock写锁介绍
加锁过程
- WriteLock的lock()内部通过sync.acquire(1)获取锁。
- 尝试通过tryAcquire获取写锁,如果获取成功那么就成功占用写锁。
- 获取写锁失败后,将当前线程添加到写锁唤醒队列当中acquireQueued(addWaiter(Node.EXCLUSIVE), arg))。
- acquireQueued(addWaiter(Node.EXCLUSIVE), arg))的操作逻辑和ReentrantLock的逻辑一致。
public static class WriteLock implements Lock, java.io.Serializable { private static final long serialVersionUID = -4992448646407690164L; private final Sync sync; protected WriteLock(ReentrantReadWriteLock lock) { sync = lock.sync; } public void lock() { sync.acquire(1); } } public final void acquire(int arg) { // 1、先尝试获取锁tryAcquire // 2、获锁失败就addWaiter操作 // 3、acquireQueued判断是否唤醒 if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
tryAcquire过程
- 获取锁状态state变量,并获取写锁占用的计数值。
- 当前state不为0,如果写锁状态为0说明读锁被占用,返回锁占用失败。
- 锁状态state不为空且占锁线程为当前线程,说明锁被其他线程占用返回锁占用失败。
- 写锁重入数溢出,返回锁占用失败。
- 如果写锁阻塞 或者 设置state状态失败,返回锁占用失败。
- 设置当前锁占用线程为当前线程,返回锁占用成功。
protected final boolean tryAcquire(int acquires) { Thread current = Thread.currentThread(); // 获取锁状态state变量 int c = getState(); // 获取写锁占用的计数 int w = exclusiveCount(c); // 如果锁状态state不为0 if (c != 0) { // 1、当前state不为0,如果写锁状态为0说明读锁此时被占用,说明锁被读锁占用 // 2、锁状态state不为空且占锁线程为当前线程(属于锁重入),说明锁被其他线程占用 if (w == 0 || current != getExclusiveOwnerThread()) return false; // 写锁重入数溢出 if (w + exclusiveCount(acquires) > MAX_COUNT) throw new Error("Maximum lock count exceeded"); // 写锁获取成功返回成功标记 setState(c + acquires); return true; } // 如果写锁阻塞 或者 设置state状态失败,那么就代表获锁失败 if (writerShouldBlock() || !compareAndSetState(c, c + acquires)) return false; // 设置当前锁占用线程为当前线程 setExclusiveOwnerThread(current); return true; }
解锁过程
- WriteLock的unlock()内部通过sync. release(1)释放锁。
- 尝试通过tryRelease()方法来释放锁并唤醒下一个等待线程。
- 在唤醒过程中需要仔细看看读写锁等待线程唤醒的细节,待补充
public void unlock() { sync.release(1); } public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
tryRelease过程
- 判断当前线程和锁占用线程不一致isHeldExclusively()抛出异常。
- 锁状态减去当前释放动作传入参数nextc = getState() - releases。
- 判断锁状态的写状态为0就表明当前线程已经完全释放锁。
- 当前线程完全释放锁,然后设置锁占用线程为null并设置锁状态。
protected final boolean tryRelease(int releases) { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); int nextc = getState() - releases; boolean free = exclusiveCount(nextc) == 0; if (free) setExclusiveOwnerThread(null); setState(nextc); return free; }
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
第三章 Java的基础程序设计结构
一个简单的 Java 应用程序 访问修饰符 public,private,protected main 方法必须时public修饰的,C#则不必须 数据类型 可以用16进制表示浮点数 可以用2,8,16进制表示整数 Double.POSITIVE_INFINITY,Double.NEGATIVE_INFINITY,DOUBLE.NaN 分别表示正无穷,负无穷,不是数值; if(x==Double.NaN) 永不成立,可以使用Double.isNaN(x)判断 强烈建议不要在程序中使用char,除非必须,可以使用String替代 运算符 整数被0除会抛出异常,浮点数则会得到无穷大或NaN结果 最初的JVM计算浮点时规定必须截断,这样在不同类型机器上可以得到一致的结果,但后来这点被修改, JVM设计者允许中间结果采用扩展的精度. 但是经过strictfp修饰的方法/类必须截断. 使用strictfp的方式可能产生溢出, 但不属于什么大问题. Math.floorMod 是为了解决有关整数余数的问题,即计算机设计中负数的余数为负数导致的不方便 StrictMath类能提供比Math更精确,更...
- 下一篇
Spring-data-jpa EntityManager封装通用Dao
由于在项目中添加新的业务,该项目使用jpa 但是表结构不是这个项目产生,所以使用jpa实体管理器(EntityManager)封装了一个通用的Dao sql封装类 package com.ronnie.data.utils; import org.springframework.util.StringUtils; /** * @Description: * @Author: rongyu * @CreateDate: 2018/8/30$ 11:49$ * @Remark: */ public class NativeSqlBuilder { private final static String BLANK = " "; private final static String COMMA = ","; private final static String DEFAULT_SELECT_FIELD = "*"; private final static String DEFAULT_ORDER = "id DESC"; private String tableName; privat...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- 设置Eclipse缩进为4个空格,增强代码规范
- CentOS6,CentOS7官方镜像安装Oracle11G
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- SpringBoot2全家桶,快速入门学习开发网站教程
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- MySQL8.0.19开启GTID主从同步CentOS8