AQS之ReentrantReadWriteLock写锁

用法

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 源码中的属性与方法在上一篇文章中已经讲过了

获取写锁源码分析

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.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。

获取锁失败的话,将当前线程进行放入阻塞队列中。

释放写锁源码分析

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; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwyjzd.html