读写锁,可以加读锁,也可以加写锁,适用于读多写少的场景。
如果有人在读数据,别人也能读数据。
如果有人在读数据,别人不能写数据。
如果有人在写数据,别人不能写数据。
如果有人在写数据,别人不能读数据。
读写状态的设计:
写锁加锁逻辑
protected WriteLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
public void lock() {
sync.acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
//写锁获取的次数,state二进制值的高16位代表了读锁,低16位代表了写锁。
int w = exclusiveCount(c);
//判断锁是否已经被占有
if (c != 0) {
//w == 0,说明有人加了读锁,没有人加写锁。加写锁失败。
//w != 0,说明有人加了写锁,如果当前线程是之前加锁的线程(可重入线程),加写锁成功。
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
setState(c + acquires);
return true;
}
//非公平锁,尝试加锁。设置state=1
//公平锁,判断如果队列中有等待线程,就不加锁。
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
读锁的加锁逻辑
读锁不是独占式锁,即同一时刻该锁可以被多个读线程获取也就是一种共享式锁。
protected ReadLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
public void lock() {
sync.acquireShared(1);
}
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
//exclusiveCount(c) != 0,说明有人加了写锁,并且还不是当前线程加的,此时不能加读锁。
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
//读锁的加锁次数
int r = sharedCount(c);
if (!readerShouldBlock() &&r < MAX_COUNT &&
//当前线程获取读锁
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
//如果CAS失败或者已经获取读锁的线程再次获取读锁
return fullTryAcquireShared(current);
}