Java—多线程8 ReentranReadWriteLock可重入读写锁
读写者模型
读写锁允许同一时刻被多个读线程访问,但在写线程访问时,所有的读线程以及其他写线程均会被阻塞。
写锁是一个独占锁:
读锁!= 无锁:
如果 读锁==无锁,当有写线程时,读线程不会停止。
如果 读锁!=写锁 当有写线程访问时,读线程会被停止
写锁—独占锁
1.写锁的获取:
protected final boolean tryAcquire(int acquires) {
/*
* Walkthrough:
* 1. If read count nonzero or write count nonzero
* and owner is a different thread, fail.
* 2. If count would saturate, fail. (This can only
* happen if count is already nonzero.)
* 3. Otherwise, this thread is eligible for lock if
* it is either a reentrant acquire or
* queue policy allows it. If so, update state
* and set owner.
当前的读写锁没有被任何读线程获取并且写锁要么是0,要么被你自己获取
*/
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;
}
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
//此时读写状态为0,写锁可以正常获取到同步状态
//当前线程置为写锁线程
setExclusiveOwnerThread(current);
return true;
}
如何区分读状态和写状态
同步状态的高16位标书读锁的次数;同步状态的低16位表示写锁的次数
2.写锁的释放
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//同步状态减去写锁状态
int nextc = getState() - releases;
//当前写锁状态是否为0,为0则释放写锁
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
//不为0则更新同步状态
setState(nextc);
return free;
}
读锁—共享锁(一般与独占锁搭配使用实现读写者模型)
获取:只要当前没有写线程获取到写锁,并且读锁的获取次数不超过最大值,读锁就可以获取成功。
释放:自旋,将同步状态减去读状态即可,如果减到0,就可以释放锁
应用场景:所有缓存的实现必有读写锁,缓存一定是个共享资源,
写锁的降级
写锁可以降级成写锁,写锁不能降级成读锁。