AQS之ReentrantReadWriteLock写锁分析
1. 用法
1.1 定义一个安全的list集合
public class LockDemo {ArrayList<Integer> arrayList = new ArrayList<>();//定义一个集合// 定义读锁ReentrantReadWriteLock.ReadLock readLock = new ReentrantReadWriteLock(true).readLock();// 定义写锁ReentrantReadWriteLock.WriteLock writeLock = new ReentrantReadWriteLock(true).writeLock();public void addEle(Integer ele) {writeLock.lock(); // 获取写锁arrayList.add(ele);writeLock.unlock(); // 释放写锁}public Integer getEle(Integer index) {try{readLock.lock(); // 获取读锁Integer res = arrayList.get(index);return res;} finally{readLock.unlock();// 释放读锁}}}1.2 Sync 源码中的属性与方法在上一篇文章中已经讲过了
2. 获取写锁源码分析
ReentrantReadWriteLock中的lock方法
public void lock() {sync.acquire(1);}AbstractQueuedSynchronizer中的acquire方法
public final void acquire(int arg) { // 获取锁失败则进入阻塞队列if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}acquireQueued(addWaiter(Node.EXCLUSIVE), arg))****,中的acquireQueued方法和addWaiter方法在前面的文章中都已经进行了详细的解释说明。

ReentrantReadWriteLock中的tryAcquire方法
protected final boolean tryAcquire(int acquires) { // 获取当前线程 Thread current = Thread.currentThread(); // 获取状态 int c = getState(); // 计算写线程数量就是独占锁的可从入数量 int w = exclusiveCount(c); // 当前同步状态state != 0,说明已经有其余线程获取了读锁或者写锁 if (c != 0) { // 当前state不为0,此时:假如写锁状态为0说明读锁此时被占用返回false; // 假如写锁状态不为0且写锁没有被当前线程持有返回false if (w == 0 || current != getExclusiveOwnerThread()) return false; // 判断同一线程获取写锁能否超过最大次数(65535),支持可重入 if (w + exclusiveCount(acquires) > MAX_COUNT) throw new Error("Maximum lock count exceeded"); //升级状态 //此时当前线程已持有写锁,现在是重入,所以只要要修改锁的数量就可 setState(c + acquires); return true; } //到这里说明此时c=0,读锁和写锁都没有被获取 //writerShouldBlock表示能否阻塞 if (writerShouldBlock() || !compareAndSetState(c, c + acquires)) return false; // 设置锁为当前线程所有 setExclusiveOwnerThread(current); return true;}static final class FairSync extends Sync {// 写锁能否应该被阻塞 final boolean writerShouldBlock() {return hasQueuedPredecessors();}}3. 获取写锁流程图
3.1 流程图获取写锁过程

3.2 流程图获取写锁过程解析
写锁的获取过程如下:
- 首先获取c、w。c表示当前锁状态;w表示写线程数量。而后判断同步状态state能否为0。假如state!=0,说明已经有其余线程获取了读锁或者写锁。
- 假如锁状态不为零(c != 0),而写锁的状态为0(w = 0),说明读锁此时被其余线程占用,所以当前线程不能获取写锁,自然返回false。或者者锁状态不为零,而写锁的状态也不为0,但是获取写锁的线程不是当前线程,则当前线程也不能获取写锁。
- 判断当前线程获取写锁能否超过最大次数,若超过,抛异常,反之升级同步状态(此时当前线程已获取写锁,升级是线程安全的),返回true。
- 假如state为0,此时读锁或者写锁都没有被获取,判断能否需要阻塞(公平和非公平方式实现不同),在非公平策略下总是不会被阻塞,在公平策略下会进行判断(判断同步队列中能否有等待时间更长的线程,若存在,则需要被阻塞,否则,无需阻塞),假如不需要阻塞,则CAS升级同步状态,若CAS成功则返回true,失败则说明锁被别的线程抢去了,返回false。假如需要阻塞则也返回false。
- 成功获取写锁后,将当前线程设置为占有写锁的线程,返回true。
- 获取锁失败的话,将当前线程进行放入阻塞队列中。
4. 释放写锁源码分析
ReentrantReadWriteLock中的unlock方法
public void unlock() { sync.release(1);}AbstractQueuedSynchronizer中的release方法
public final boolean release(int arg) { // 假如返回true 那么释放成功了 if (tryRelease(arg)) { Node h = head; // 假如头部不为空,并且头节点的waitStatus是唤醒状态那么唤醒后继线程 if (h != null && h.waitStatus != 0) // 唤醒后继线程 unparkSuccessor(h); return true; } return false;}ReentrantReadWriteLock中tryRelease方法
protected final boolean tryRelease(int releases) {// 若锁的持有者不是当前线程,抛出异常 if (!isHeldExclusively()) // 非法的监控器异常 throw new IllegalMonitorStateException(); // 计算写锁的新线程数 int nextc = getState() - releases; // 假如独占模式重入数为0了,说明独占模式被释放 boolean free = exclusiveCount(nextc) == 0; if (free) // 设置独占线程为空 setExclusiveOwnerThread(null); // 设置写锁的新线程数 // 不论独占模式能否被释放,升级独占重入数 setState(nextc); return free;}protected final boolean isHeldExclusively() { // 若当前线程是当前锁的持有线程那么返回true return getExclusiveOwnerThread() == Thread.currentThread();}5. 释放写锁流程图
5.1 流程图释放过程

5.2 流程图释放过程解析
写锁的释放过程:
- 首先查看当前线程能否为写锁的持有者,假如不是抛出异常。而后检查释放后写锁的线程数能否为0,假如为0则表示写锁空闲了,释放锁资源将锁的持有线程设置为null,否则释放仅仅只是一次重入锁而已,并不能将写锁的线程清空。
- 说明:此方法用于释放写锁资源,首先会判断该线程能否为独占线程,若不为独占线程,则抛出异常,否则,计算释放资源后的写锁的数量,若为0,表示成功释放,资源不将被占用,否则,表示资源还被占用。
6. 总结
6.1 state 解析
private volatile int state;int 类型占有 4个字节一个字节8位,所以 state 一个 32 位,高 16 位 代表读锁 低 16 位代表 写锁。
// 0x0000FFFF 16 进制// 1111111111111111 2 进制// 65535 10 进制static final int SHARED_SHIFT = 16;static final int SHARED_UNIT = (1 << SHARED_SHIFT); // 65536static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; //65535 // 1111111111111111static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; // 65535 // 1111111111111111假如此时同步状态位 c 那么获取写状态 c & EXCLUSIVE_MASK
假如此时同步状态位 c 那么获取读状态 c >>>16 无符号补0,右移16位
作者:雪中孤狼
链接:https://www.cnblogs.com/xyzmy/p/14314078.html
说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » AQS之ReentrantReadWriteLock写锁分析
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » AQS之ReentrantReadWriteLock写锁分析