ReentrantReadWriteLock 是通过加锁来保证线程安全的,它是 Java 源码中内置的加锁的类
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
try {
lock.writeLock().lock();
} finally {
lock.writeLock().unlock();
}
那么它内部是通过什么方式来实现加锁的呢,通过以下源码中的注释便可清楚。
public ReentrantReadWriteLock() {
this(false); // 默认是 false
}
public ReentrantReadWriteLock(boolean fair) {
// 默认是不公平锁
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
先来看 WriteLock 的
// WriteLock
public void lock() {
sync.acquire(1);
}
// Sync继承自 AbstractQueuedSynchronizer, acquire 方法是在 AbstractQueuedSynchronizer 里实现的
public final void acquire(int arg) {
// 主要控制同步的逻辑在 tryAcquire
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt(); // 如果没有获取到锁,当前线程中断
}
// Sync 类中 tryAcquire 方法的实现
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState(); // 持有该锁的数量
int w = exclusiveCount(c);
if (c != 0) {
// 已经有持有该锁的
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;
}
// 还没有持有该锁的,如果写数量失败了,就返回 false
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
// 设置持有锁的线程是当前线程
setExclusiveOwnerThread(current);
return true;
}
到这里加锁主要逻辑就结束了,那么解锁的呢?接着往下看,解锁跟加锁的过程比较类似
// WriteLock
public void unlock() {
sync.release(1);
}
// Sync继承自 AbstractQueuedSynchronizer, acquire 方法是在 AbstractQueuedSynchronizer 里实现的
public final boolean release(int arg) {
// 解锁的主要逻辑在 tryRelease
if (tryRelease(arg)) {
// ...
return true;
}
return false;
}
// Sync 类的 tryRelease 实现
protected final boolean tryRelease(int releases) {
// ...
int nextc = getState() - releases; // 剩余的锁的数量
boolean free = exclusiveCount(nextc) == 0; // 是否已经没有锁
if (free)
setExclusiveOwnerThread(null); // 如果已经没有锁,则持有线程置为 null
setState(nextc); // 设置剩余锁的数量
return free; // 返回是否已经不再持有锁
}
总结:ReentrantLock 就是通过设置当前持有锁的线程和持有锁的数量,加锁时候通过判断这些来决定是否能获取锁,如果暂时不能获取锁,则线程中断。解锁过程是修改相应的持有锁线程和持有锁数量。