1 介绍
ReentrantReadWriteLock实现了自ReadWriteLock接口,与ReentrantLock一样依赖AQS实现申请锁和释放锁。ReentrantReadWriteLock内部有写锁和读锁,在不同线程之间两种锁互斥(A线程有写锁,B线程有读锁,两个线程不能同时执行)。读锁为共享锁(读锁不进入CLH队列),写锁为独享锁。
以下是ReentrantReadWriteLock读/写锁申请锁和释放锁方法。
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
reentrantReadWriteLock.readLock().lock();//读锁申请
reentrantReadWriteLock.readLock().unlock();//释放读锁
reentrantReadWriteLock.writeLock().lock();//写锁申请
reentrantReadWriteLock.writeLock().unlock();//释放写锁
2 锁升降级
锁降级
写锁降级为读锁,不是申请了写锁再释放写锁然后申请读锁,而是先申请了写锁再申请读锁然后释放了先前的写锁。
代码分析:
写锁上锁会执行的方法:
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();//获取当前线程
int c = getState();
int w = exclusiveCount(c);//获取独享锁(写锁)状态
if (c != 0) {//当前状态不为0
// (Note: if c != 0 and w == 0 then shared count != 0)
//写锁资源为0,读锁不为0 或者当前线程不是独占线程
/**
*这里要注意的是 已经有了读锁,再要加写锁,那么当前线程必须已经获得了写锁才能上锁成功,但是如果同一个
*线程先获取了读锁(之前没有获取过写锁),再要获取写锁,此时写锁的独享线程可能是空或者其他独享线程,
*那么将会上锁失败,也是为什么升级锁失败的原因
*/
if (w == 0 || current != getExclusiveOwnerThread())
return false;//上锁失败
//写锁资源不为0且当前线程是独占线程
if (w + exclusiveCount(acquires) > MAX_COUNT)//写资源加锁之后超过了最大数
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
//上锁
setState(c + acquires);
return true;
}
//当前状态为0
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires)) //写锁应该被阻塞 或者 设置状态失败
return false;//尝试获取锁失败
//尝试获取锁成功
setExclusiveOwnerThread(current);//设置当前线程为独享线程
return true;
}
读锁上锁代码分析:
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
/**
* 独享锁不为0 并且 当前线程不是独享线程
*/
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;//上锁失败
//当前独享锁为0 或者当前线程是独享线程
int r = sharedCount(c);
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {//当前读锁不应该被阻塞
if (r == 0) {//读锁为0
firstReader = current; //当前线程为第一个读线程
firstReaderHoldCount = 1;//第一个读线程资源为1
} else if (firstReader == current) { //当前线程就是第一个读线程
firstReaderHoldCount++; //第一个读线程资源+1
} else {//读锁既不是0 也是第一个读线程
HoldCounter rh = cachedHoldCounter;//获取最后一个读线程计数器
if (rh == null || rh.tid != getThreadId(current))//最后一个读线程计数器为空或者当前线程不是最后一个读线程
cachedHoldCounter = rh = readHolds.get();//最后一个读线程的计数器为当前线程的计数器
else if (rh.count == 0)//最后一个读线程是本线程且锁读锁数量为0
readHolds.set(rh);//更新本线程的计数器
rh.count++;//计数器+1
}
return 1;
}
//自旋的形式进行上锁
return fullTryAcquireShared(current);
}
fullTryAcquireShared方法:
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) {
int c = getState();
if (exclusiveCount(c) != 0) {//独享锁不为0
if (getExclusiveOwnerThread() != current)//当前线程不是独享线程
return -1;//申请失败
// else we hold the exclusive lock; blocking here
// would cause deadlock.
} else if (readerShouldBlock()) { //当前读线程应该被阻塞
// Make sure we're not acquiring read lock reentrantly
if (firstReader == current) {//当前线程就是第一个读线程
// assert firstReaderHoldCount > 0;
} else {
//获取当前线程的计数器
if (rh == null) {
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
}
}
if (rh.count == 0)//所资源要求0
return -1;//申请失败
}
}
if (sharedCount(c) == MAX_COUNT)//所资源已经达到了最大数
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) {
if (sharedCount(c) == 0) {//共享锁为0
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {//当前线程就是第一个读线程
firstReaderHoldCount++;
} else {
//获取当前线程的计数器
if (rh == null)
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
cachedHoldCounter = rh; // cache for release
}
return 1;//上锁成功
}
}
}
根据以上代码当写锁已经获取时,同一个线程仍可以去请求读锁。但是申请到读锁之后,是不能再申请写锁的。