1 引言

读写锁,可以加读锁,也可以加写锁。

读锁和写锁是互斥的,也就是说,你加了读锁之后,就不能加写锁;如果加了写锁,就不能加读锁。但是如果有人加了读锁之后,别人可以同时加读锁。

如果你有一份数据,有人读,有人写,如果你全部都是用synchronized的话,会导致如果多个人读,也是要串行化,一个接一个的读。我们希望的效果是多个人可以同时来读,如果使用读锁和写锁分开的方式,就可以让多个人来读数据,多个人可以同时加读锁。

小结:

  • 如果有人在读数据,就不能有人写数据,读锁 -> 写锁 -> 互斥
  • 如果有人在写数据,别人不能写数据,写锁 -> 写锁 -> 互斥;如果有人在写数据,别人也不能读数据,写锁 -> 读锁 > 互斥

2 写锁实现原理

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();
  					// 获取到一个state = 0
            int c = getState();
  					// // w,剧透一下,人家读写锁,非常聪明的利用state的值
						// 二进制值里面的高低16位分别代表了读锁和写锁,AQS就一个,state
						// state二进制值的高16位代表了读锁,低16位代表了写锁
						// 可以认为下面的w就是从c(二进制值)通过位运算
						// 获取到了state的低16位,代表了写锁的状态
            int w = exclusiveCount(c);
  					// 如果c != 0,说明有人加过锁,但是此时c = 0
            if (c != 0) {
                // (Note: if c != 0 and w == 0 then shared count != 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;
            }
  					// 非公平锁,此时一定会去尝试加锁
						// 如果是公平锁,此时会判断如果队列中有等待线程,就不加锁
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
            setExclusiveOwnerThread(current);
            return true;
}
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
static final int SHARED_SHIFT   = 16;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;

3 可重入写锁实现原理

protected final boolean tryAcquire(int acquires) {
            Thread current = Thread.currentThread();
  					// 获取到一个state = 1
            int c = getState();
            int w = exclusiveCount(c);
  					// 如果c != 0,说明有人加过锁,但是此时c = 0
            if (c != 0) {
                // c != 0,w == 0,c肯定不是0,但是低16位是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;
            setExclusiveOwnerThread(current);
            return true;
}

4 读写锁的护互斥实现原理

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);
    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;
    }
    return fullTryAcquireShared(current);
}