读读并行
读写串行
写写串行
读加锁的目的,防止脏读,因为这时候要是没加锁,写数据了,那么重复读的时候会脏读。
读锁加锁
上读锁
java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock.lock(){
sync.acquire(1)
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
找到读锁的tryAcquire
int 32位, 读锁次数占高16位, 写锁占低16位。
exclusiveCount(int c) 通过 c&EXCLUSIVE_MASK 做与运算就得到了 写的次数。
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
/** Returns the number of shared holds represented in count */
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
/** Returns the number of exclusive holds represented in count */
// EXCLUSIVE_MASK 相当于 前16个0 后16个0
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
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.
*/
Thread current = Thread.currentThread();
int c = getState();
// 获取读的次数
int w = exclusiveCount(c);
// 判断当前线程的次数
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
// w为0说明,没有写操作,返回整个方法返回false,就排队
// 当前线程不等于占有的线程,同样返回false,去排队
if (w == 0 || current != getExclusiveOwnerThread())
return false;
// 预判写的重入锁过大
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
// 写锁+1
setState(c + acquires);
return true;
}
// c==0,那么写锁要不排队,要不去竞争锁
// writerShouldBlock 有队列就需要排队,否则就不需要排队
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
// 竞争成功,将锁设置为当前线程
setExclusiveOwnerThread(current);
return true;
}
读锁加锁
public void lock() {
sync.acquireShared(1);
}
tryAcquireShared(()判断是否可以获取锁
doAcquireShared()尝试获取锁,添加到队列
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
protected final int tryAcquireShared(int unused) {
/*
* Walkthrough:
* 1. If write lock held by another thread, fail.
* 2. Otherwise, this thread is eligible for
* lock wrt state, so ask if it should block
* because of queue policy. If not, try
* to grant by CASing state and updating count.
* Note that step does not check for reentrant
* acquires, which is postponed to full version
* to avoid having to check hold count in
* the more typical non-reentrant case.
* 3. If step 2 fails either because thread
* apparently not eligible or CAS fails or count
* saturated, chain to version with full retry loop.
*/
Thread current = Thread.currentThread();
int c = getState();
// 有写锁,并且不是当前线程,不能加写锁
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
// 获取当前线程读次数
int r = sharedCount(c);
// readerShouldBlock是否需要排队
// r < MAX_COUNT 饱和策略
// compareAndSetState() CAS获取读锁 并读锁+1
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) { // 0个读锁
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {// 第一个拥有读锁是当前线程
firstReaderHoldCount++;
} else {// 其他情况
// HoldCounter 线程id 和 当前线程次数
HoldCounter rh = cachedHoldCounter;
// 如果当前线程是新的,会放到缓存中,也会放在map中,最后放到缓存中
// 如果队列有当前线程,且不是缓存(last)中,也不是frist,但是在map中就需要map中取出,最后放到缓存中
// frist , last都不需要map中取, 节省时间.
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
return fullTryAcquireShared(current);
}