读读并行
读写串行
写写串行
读加锁的目的,防止脏读,因为这时候要是没加锁,写数据了,那么重复读的时候会脏读。

读锁不能加 condition
写锁可以加,因为写锁是排它锁

读锁加锁

上读锁

  1. java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock.lock(){
  2. sync.acquire(1)
  3. }
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);
}