Java JUC包下ReentrantReadWriteLock读写计数器合并为一个原理解析@TOC
通过阅读java鼻祖 Doug Lea 的并发编程中读写锁的使用原理,对于ReentrantReadWriteLock使用的Sync实现方法进行研究,同时也为了加深映像,自己实现了简单的读写锁。对于ReentrantReadWriteLock.Sync的State这个值的设计确实巧妙。
State值记录了读写锁的计数器,使用的是int类型,分为两段,前面16位记录的是当前读加锁的计数,后面16位记录的时候当前写加锁的计数,在Sync类中有计算读写锁计数的位运算。
/*
* 读/写 计数换算的常量和方法。
* 锁状态是分为两个无符号的短整型:
* 低位的代表互斥锁(写锁)持有的数量,
* 高位代表共享锁(读锁)持有的数量
*/
//共享锁的位数
static final int SHARED_SHIFT = 16;
//共享锁每次递增的量 二进制的值为 0x0001 0000
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
//读写锁分别持有的最大数量 2^16 - 1
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
//互斥锁(写锁)掩码 0x0000 FFFF
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
/** 返回共享锁的数量 */
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
/** 返回互斥锁的数量 */
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
那么问题来了,为什么在读写锁中要把读写计数器放在一个State的int值中?
先来看下获取写锁加锁的方法实现 具体实现是 sync.tryWriteLock();
/**
* Performs tryLock for write, enabling barging in both modes.
* This is identical in effect to tryAcquire except for lack
* of calls to writerShouldBlock.
*/
final boolean tryWriteLock() {
Thread current = Thread.currentThread();
int c = getState();
if (c != 0) {
int w = exclusiveCount(c);
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
}
if (!compareAndSetState(c, c + 1))
return false;
setExclusiveOwnerThread(current);
return true;
}
重点的语句是 compareAndSetState(c, c + 1) 表示在进行写加锁的时候进行CAS操作,此时把读锁的Count值也计算在内,避免了线程异常,如果自己实现读写锁的时候,肯定会把读写锁的计数器分开,这样子在进行CAS操作的时候,需要再次加锁,增加系统开销。