开篇
这篇文章主要从源码角度讲解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;
}