实际的开发过程中,会遇到这样读多写少的场景。在这种场景中,独占锁都是阻塞的,而实际变量读的时候变量是可以共享的,如果使用独占锁的话,就有点大材小用了。在Java并发包还有个强大的工具那就是ReadWriteLock,来解决这种场景的问题。
ReadWriteLock是个接口readLock()和writeLock()方法来返回锁对象;
Lock readLock();
Lock writeLock();
来看下具体的实现类ReentrantReadWriteLock
两个构造方法同样实现公平锁和非公平锁:
无参构造方法,默认实现的是非公平锁:
public ReentrantReadWriteLock() {
this(false);
}
含参数的构造方法:根据参数的传递支持实现公平锁和非公平锁:
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
五个内部类:
Sync:实现了队列同步器AbstractQueuedSynchronizer,锁的功能实现
NonfairSync:非公平锁继承了Sycn,锁功能的入口
FairSync:公平锁继承了Sync,锁功能的入口
ReadLock:读锁,实现了Lock
WriteLock:写锁,实现了Lock
在内部类Sync同样存在几个重要的方法需要提前了解,对于理解源码有很大的帮助:
内部类:
ThreadLocalHoldCounter:线程本本地变量的子类继承了ThreadLocal,存储线程的计数器
HoldCounter:每线程读取保持计数的计数器,作为ThreadLocal维护;缓存在cachedHoldCounter中。
包含count来计算线程数,使用id tid 主要用来避免垃圾回收
成员变量:
//当前线程持有的可重入读锁数。仅在构造函数和readObject中初始化。每当线程的读取保持计数
//降至0时删除。
private transient ThreadLocalHoldCounter readHolds;
//用来缓存最后一个获取读锁的线程的读锁的重入次数,存放在线程本地变量中
private transient HoldCounter cachedHoldCounter;
//第一个获取读锁的线程,并且未释放锁
private transient Thread firstReader = null;
//第一个获取读锁的线程读书重入次数
private transient int firstReaderHoldCount;
常量:
//将32位的int变量通过位运算一分为二,高16位用于共享模式,低16位用于独占模式,
//表示占有锁的线程数量
static final int SHARED_SHIFT = 16;//读锁的站位数,常量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;//写锁的占有线程数量
静态方法:
//用来取c的低16位,表示读锁的获取次数,包括重入的情况
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
//用来取c的低16位,表示写锁的重入次数
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
非公平锁读锁与写锁实现机制
加锁方法:
public void lock() {
sync.acquireShared(1);
}
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)(1)Sync重写了该方法(判断写锁,如果<0,表示写锁不支持占有,加入对列中,)
doAcquireShared(arg);(2)加锁失败,加入到等待队列中
}
//1、共享模式
protected final int tryAcquireShared(int unused) {
//获取当前线程
Thread current = Thread.currentThread();
//获取线程状态
int c = getState();
//判断写锁是否被其他线程占有,如果占有返回为-1,执行2加入等待队列
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
//共享锁的实现机制
int r = sharedCount(c);//获取当前获取读锁的线程数(也就是读锁的获取次数)
//如果不需要阻塞,同时获取读锁的数量小于阈值而且通过CAS加锁成功,
//进行第个获取线程变量或者缓存更新
if (!readerShouldBlock() &&//1-1
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {//获取读锁数量为0,表示读锁还没未被持有,更新变量firstReader为
//当前线程,firstReaderHoldCount持有锁的数量为1
firstReader = current;
firstReaderHoldCount = 1;
//如果当前线程就是第一个获取读锁的线程,则获取线程数量+1
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
//如果不是第一个获取线程的现在加锁成功,获取当前的线程计数器
HoldCounter rh = cachedHoldCounter;
//如果当前线程技术器为null或者不是当前线程的技术器
//直接从线程本地变量中获取
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
//如果线程计数器中count为0则直接放入缓存readHolds中
readHolds.set(rh);
rh.count++;//count加1
}
return 1;//返回1加锁成功
}
//这里有三个条件:1.不需要阻塞2.读锁超过阈值3.加锁失败
return fullTryAcquireShared(current);1-2
}
1-2
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
//自旋判断,加锁成功或者加锁失败停止循环
for (;;) {
//再次判断是否写锁的占有者
int c = getState();
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
// else we hold the exclusive lock; blocking here
// would cause deadlock.
} else if (readerShouldBlock()) {//如果返回为true,表示写锁正在被占有或者等待队列中有其他线程正在等待获取写锁
// Make sure we're not acquiring read lock reentrantly
if (firstReader == current) {//判断当前线程如果是首次获取读锁的线程则直接执行后面的操作
// assert firstReaderHoldCount > 0;
} else {
//如果不是第一个获取线程的现在加锁成功,获取当前的线程计数器
if (rh == null) {
//如果当前线程技术器为null或者不是当前线程的技术器
//直接从线程本地变量中获取
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
//如果count是0说明仍然是初始化的结果,直接删除
if (rh.count == 0)
readHolds.remove();
}
}
//仅是初始化的缓存,返回-1继续执行加入队列的操作
if (rh.count == 0)
return -1;
}
}
//读锁的数量超过阈值直接抛错
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
//再次尝试加锁,加锁成功,则判断是否是首次获取读锁,如果是则更新首次获取线程的变量
//如果不是则更新缓存及及缓存中读锁的数量(缓存只是最后一个获取读锁的线程和获取读锁的数量)
if (compareAndSetState(c, c + SHARED_UNIT)) {
if (sharedCount(c) == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
if (rh == null)
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
cachedHoldCounter = rh; // cache for release
}
return 1;
}
}
}
2加入等待队列
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);//加入到等待队列中,返回当前节点(如果首节点为空,则设置一个不包含线程及状态的首节点)
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();//获取当前线程的前驱节点
if (p == head) {
int r = tryAcquireShared(arg);//如果前驱节点为首节点,则在此尝试加锁
if (r >= 0) {
setHeadAndPropagate(node, r);2-1设置首节点和节点传播
p.next = null; // help GC
if (interrupted)
selfInterrupt();//阻塞当前线程
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
5-2
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);//将当前节点从等待队列中剔除
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();5-3
}
}
5-3自旋修改节点状态
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {//如果首节点不为空,且节点不是尾节点
int ws = h.waitStatus;//获取节点状态
if (ws == Node.SIGNAL) {//如果节点是阻塞状态,CAS修改节状态为0
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);//修改失败,则唤醒线程
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))//
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
释放共享读锁
public void unlock() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {//1.释放锁及修改缓存中的值
doReleaseShared();//2.唤醒线程
return true;
}
return false;
}
//1.
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {//如果当前线程是获取读锁的一个线程
// assert firstReaderHoldCount > 0;
if (firstReaderHoldCount == 1)//此时获取读锁是1,解锁之后就不在持有锁,更新为null
firstReader = null;
else
firstReaderHoldCount--;//不是1则修改数量减1
} else {
//不是第一个获取读锁的线程
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))//绑定计数器的缓存不是当前线程,则直接从本地线程变量中获取
rh = readHolds.get();
int count = rh.count;//获取持有读锁数量的count
if (count <= 1) {//如果小于等于1,直接从缓存中删除
readHolds.remove();
if (count <= 0)//如果小于等待0 则抛错
throw unmatchedUnlockException();
}
--rh.count;//持锁数量减1
}
//自旋修改线程状态
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))CAS修改锁的状态
// Releasing the read lock has no effect on readers,
// but it may allow waiting writers to proceed if
// both read and write locks are now free.
return nextc == 0;//唤醒写锁的等待线程
}
}
2.唤醒线程
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
写锁加锁
public void lock() {
sync.acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&//1加锁成功返回false,加锁失败加入等待队列中
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();//阻塞线程
}
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();//锁状态
int w = exclusiveCount(c);//获取重入次数
if (c != 0) {//
//c!=0同时w==0表示写锁已经被其他线程持有
//c!=0同时current != getExclusiveOwnerThread()写锁已经被其他线程持有
// (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() ||//非公平锁返回false或加锁失败,则返回false进入阻塞队列
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);//设置当前线程为占用线程
return true;
}
写锁的解锁
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {//1.修改锁状态成功
Node h = head;//取等待节点的首节点
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);//唤醒线程
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())//执行线程必须是占用线程
throw new IllegalMonitorStateException();
int nextc = getState() - releases;//锁定的次数减1
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
setState(nextc);//重新修改锁的数量
return free;
}