目录
一、定义
二、使用
三、WriteLock
1、lock / lockInterruptibly
2、tryLock
3、unlock
四、ReadLock
1、lock
2、lockInterruptibly
3、tryLock
4、unlock
五、NonfairSync / FairSync
六、AbstractQueuedSynchronizer实现总结
ReentrantReadWriteLock是一个可重入的,支持读写分离的锁,本篇博客就详细探讨该类的实现。
ReentrantReadWriteLock用于实现ReadWriteLock接口,该接口定义的方法如下:
该类包含多个内部类,如下:
ReadLock和WriteLock两个都是基于Sync实现的Lock接口,只是调用Sync的方法不同而已。另外两个类FairSync和NonfairSync都继承自Sync,分别表示公平锁和非公平锁,其类继承关系如下:
FairSync和NonfairSync都是只是实现了父类的两个抽象方法,writerShouldBlock和readerShouldBlock方法,这两方法用于判断写操作是否需要阻塞,读操作是否需要阻塞。
Sync内部还有两个内部类,其定义如下:
HoldCounter是一个数据结构用来记录每个线程的重入读锁次数和关联的线程ID, ThreadLocalHoldCounter表示一个存储HoldCounter实例的本地线程变量。多个线程可同时获取读锁,他们获取的读锁实际都是同一个Sync实例,Sync只有一个state属性用来保存重入读锁的总次数,当该次数变成0的时候才能真正释放读锁。因为获取读锁的线程都需要执行unlock方法释放锁,所以必须记录每个线程自身重入读锁的次数,使用ThreadLocalHoldCounter,即下面的readHolds属性来实现。
Sync中包含的属性如下:
//本地线程变量,保存当前线程累计重入读锁的次数
private transient ThreadLocalHoldCounter readHolds;
//缓存的上一次成功获取读锁的HoldCounter
private transient HoldCounter cachedHoldCounter;
//第一个获取读锁的线程
private transient Thread firstReader = null;
//第一个获取读锁的线程,累计重入读锁的次数
private transient int firstReaderHoldCount;
除此之外,Sync定义了几个常量,用来计算重入读锁或者写锁的累计次数,如下:
因为一个线程在获取写锁后可以再获取读锁,为了在state属性中同时记录重入这两种锁的次数,Sync只有这一个属性保存锁重入次数,就将state属性拆成两半,高16位的数值用于表示读锁的重入次数,低16位的值表示写锁的重入次数,因为有位数限制,所以重入的最大次数不能超过上面的MAX_COUNT。
ReentrantReadWriteLock本身的属性如下:
//读锁实例
private final ReentrantReadWriteLock.ReadLock readerLock;
//写锁实例
private final ReentrantReadWriteLock.WriteLock writerLock;
//完成实际同步动作的Sync实例
final Sync sync;
private static final sun.misc.Unsafe UNSAFE;
//Thread类的tid属性的偏移量,该属性实际是关联的JVM中的JavaThread实例的指针
private static final long TID_OFFSET;
两个static属性通过static代码块完成初始化,如下:
另外三个属性是在构造函数中初始化的,构造函数的实现如下:
public ReentrantReadWriteLock() {
this(false);
}
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
下面就以读锁和写锁的具体实现为入口来分析内部类Sync的相关实现,因为其底层都是基于AbstractQueuedSynchronizer,涉及的相关方法可以参考上一篇《Java8 ReentrantLock 源码解析》。
ReentrantReadWriteLock的使用,核心还是基于其writeLock和readLock返回的写锁和读锁,跟ReentrantLock的使用是一样,如下:
@Test
public void test() throws Exception {
ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
Lock readLock=lock.readLock();
CyclicBarrier cyclicBarrier=new CyclicBarrier(6);
Runnable a=new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" start,time->"+System.currentTimeMillis());
try {
readLock.lock();
Thread.sleep(1000);
System.out.println("do something end for read,time->"+System.currentTimeMillis());
//读锁时因为读锁是共享锁,其他线程都能成功获取锁,所以不会出现死锁问题
cyclicBarrier.await();
}catch (Exception e){
e.printStackTrace();
}finally {
readLock.unlock();
}
}
};
for(int i=0;i<5;i++){
new Thread(a).start();
}
long start=System.currentTimeMillis();
cyclicBarrier.await();
System.out.println("read end,time->"+(System.currentTimeMillis()-start));
Lock writeLock=lock.writeLock();
Runnable b=new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" start,time->"+System.currentTimeMillis());
try {
writeLock.lock();
Thread.sleep(1000);
System.out.println("do something end for write,time->"+System.currentTimeMillis());
}catch (Exception e){
e.printStackTrace();
}finally {
//先unlock解锁,在await等待,避免形成死锁
writeLock.unlock();
try {
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
for(int i=0;i<5;i++){
new Thread(b).start();
}
start=System.currentTimeMillis();
cyclicBarrier.await();
System.out.println("write end,time->"+(System.currentTimeMillis()-start));
}
上述用例的输出如下:
//5个读操作线程依次启动
Thread-0 start,time->1585987927568
Thread-1 start,time->1585987927568
Thread-3 start,time->1585987927568
Thread-2 start,time->1585987927568
Thread-4 start,time->1585987927569
//几乎是同一时间获取读锁,然后完成读操作
do something end for read,time->1585987928568
do something end for read,time->1585987928569
do something end for read,time->1585987928569
do something end for read,time->1585987928569
do something end for read,time->1585987928569
//读锁是共享锁,整体耗时基本等于单个线程耗时,即sleep的耗时
read end,time->1018
//5个写操作线程依次启动
Thread-5 start,time->1585987928571
Thread-6 start,time->1585987928571
Thread-7 start,time->1585987928571
Thread-8 start,time->1585987928571
Thread-9 start,time->1585987928571
//写锁是排他锁,5个线程依次获取锁
do something end for write,time->1585987929571
do something end for write,time->1585987930571
do something end for write,time->1585987931571
do something end for write,time->1585987932571
do something end for write,time->1585987933571
//总耗时是5个写操作线程的累计耗时
write end,time->5000
上述用例中读锁似乎没啥价值,因为不加锁的效果跟加读锁的一样。读锁的价值不在于共享,而在于可以排斥写锁,即获取了读锁以后就不能再获取写锁了,必须等读锁释放了才能获取读锁,测试用例如下:
@Test
public void test2() throws Exception {
ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
Lock readLock=lock.readLock();
Lock writeLock=lock.writeLock();
CountDownLatch writeCount=new CountDownLatch(5);
Runnable a=new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" start,time->"+System.currentTimeMillis());
try {
readLock.lock();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+" do something end for read,time->"+System.currentTimeMillis());
try{
//写锁必须等待所有的读锁释放才能获取锁,此处是读锁获取完成,等待获取写锁
//就形成了死锁
writeLock.lock();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+" do something end for write,time->"+System.currentTimeMillis());
}finally {
writeLock.unlock();
}
}catch (Exception e){
e.printStackTrace();
}finally {
readLock.unlock();
writeCount.countDown();
}
}
};
for(int i=0;i<5;i++){
new Thread(a).start();
}
long start=System.currentTimeMillis();
writeCount.await();
System.out.println("read end,time->"+(System.currentTimeMillis()-start));
}
该用例的输出如下:
所有线程都卡在获取写锁哪一步了,形成了死锁。注意获取写锁后,可以再次获取读锁,因为获取写锁的只能是一个线程,已经获取了写锁再获取读锁不影响读锁的语义,测试用例如下:
@Test
public void test3() throws Exception {
ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
Lock readLock=lock.readLock();
Lock writeLock=lock.writeLock();
CountDownLatch writeCount=new CountDownLatch(5);
Runnable a=new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" start,time->"+System.currentTimeMillis());
try {
writeLock.lock();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+" do something end for write,time->"+System.currentTimeMillis());
try{
readLock.lock();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+" do something end for read,time->"+System.currentTimeMillis());
}finally {
readLock.unlock();
}
}catch (Exception e){
e.printStackTrace();
}finally {
writeLock.unlock();
writeCount.countDown();
}
}
};
for(int i=0;i<5;i++){
new Thread(a).start();
}
long start=System.currentTimeMillis();
writeCount.await();
System.out.println("write end,time->"+(System.currentTimeMillis()-start));
}
该用例的输出如下:
5个线程都是按照先获取写锁,获取读锁,释放读锁,释放写锁的顺序执行的,最后的总耗时是所有线程两个sleep的累加时间。
上述场景是一个线程内获取了写锁再获取读锁,如果是不同的线程了?比如线程A占用写锁,此时线程B获取读锁可以获取成功么?测试用例如下:
@Test
public void test4() throws Exception {
ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
Lock readLock=lock.readLock();
Lock writeLock=lock.writeLock();
CountDownLatch writeCount=new CountDownLatch(2);
Thread a=new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" start,time->"+System.currentTimeMillis());
try {
writeLock.lock();
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+" do something end for write,time->"+System.currentTimeMillis());
}catch (Exception e){
e.printStackTrace();
}finally {
writeLock.unlock();
writeCount.countDown();
}
}
});
//让线程a先运行起来,持有写锁并sleep
a.start();
Thread.sleep(100);
Thread b=new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" start,time->"+System.currentTimeMillis());
try {
readLock.lock();
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+" do something end for read,time->"+System.currentTimeMillis());
}catch (Exception e){
e.printStackTrace();
}finally {
readLock.unlock();
writeCount.countDown();
}
}
});
b.start();
long start=System.currentTimeMillis();
writeCount.await();
System.out.println("main end,time->"+(System.currentTimeMillis()-start));
}
@Test
public void test5() throws Exception {
ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
Lock readLock=lock.readLock();
Lock writeLock=lock.writeLock();
CountDownLatch writeCount=new CountDownLatch(2);
Thread a=new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" start,time->"+System.currentTimeMillis());
try {
readLock.lock();
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+" do something end for read,time->"+System.currentTimeMillis());
}catch (Exception e){
e.printStackTrace();
}finally {
readLock.unlock();
writeCount.countDown();
}
}
});
//让线程a先运行起来,持有读锁并sleep
a.start();
Thread.sleep(100);
Thread b=new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" start,time->"+System.currentTimeMillis());
try {
writeLock.lock();
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+" do something end for write,time->"+System.currentTimeMillis());
}catch (Exception e){
e.printStackTrace();
}finally {
writeLock.unlock();
writeCount.countDown();
}
}
});
b.start();
long start=System.currentTimeMillis();
writeCount.await();
System.out.println("main end,time->"+(System.currentTimeMillis()-start));
}
上述两个测试用例的执行结果如下:
//test4方法的输出
Thread-0 start,time->1585991389197
Thread-1 start,time->1585991389294
Thread-0 do something end for write,time->1585991391197
Thread-1 do something end for read,time->1585991393197
main end,time->3903
//test5方法的输出
Thread-0 start,time->1585991448174
Thread-1 start,time->1585991448275
Thread-0 do something end for read,time->1585991450174
Thread-1 do something end for write,time->1585991452174
main end,time->3900
总结一下,假如有多个线程,如果A线程占有读锁,其他线程都抢占同一个读锁,抢占成功,如果抢占写锁,无论是A线程还是其他线程都会抢占失败,必须等待读锁释放;如果A线程占有写锁,其他线程无论抢占读锁还是写锁都是抢占失败,必须等待写锁释放,此时只有A线程自己可以获取读锁或者写锁。
WriteLock和ReadLock都是实现Lock接口,具体接口的含义可以参考上一篇《Java8 ReentrantLock 源码解析》,不再重复描述。ReentrantReadWriteLock中也定义了用来获取等待链表状态的方法如getQueuedThreads,getWaitingThreads,getQueueLength,getWaitQueueLength,其实现和ReentrantLock一致,也不再重复了,重点关注WriteLock和ReadLock的接口实现。注意WriteLock的newCondition接口的实现跟ReentrantLock一样,返回一个ConditionObject实例,而ReadLock的newCondition接口会抛出UnsupportedOperationException异常。
这两方法都直接依赖于Sync的tryAcquire方法,返回true表示抢占成功,返回false表示抢占失败。
public void lock() {
sync.acquire(1);
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public final void acquire(int arg) {
//tryAcquire由子类实现,会尝试获取锁,如果获取失败返回false,
if (!tryAcquire(arg) &&
//acquireQueued方法会将当前线程阻塞直到获取锁为止,如果返回true表示线程被中断过
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//将当前线程的中断标识置为true
selfInterrupt();
}
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
//doAcquireInterruptibly的逻辑和acquireQueued差不多,区别在于前者被中断后会继续循环,后者会抛出异常
doAcquireInterruptibly(arg);
}
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
if (c != 0) {
//c不等于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");
//更新state
setState(c + acquires);
return true;
}
//c等于0,即该锁未被占用,如果writerShouldBlock返回true则直接返回false,放入等待获取锁的同步链表中等待被唤醒
//如果writerShouldBlock返回false,则执行后面的compareAndSetState方法原子修改state
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
//如果compareAndSetState原子修改state成功,即抢占锁成功,则设置当前线程为占有锁的线程
setExclusiveOwnerThread(current);
return true;
}
//取c的低16位的值
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
tryLock依赖于Sync的tryWriteLock和tryAcquire方法
public boolean tryLock( ) {
return sync.tryWriteLock();
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
//tryWriteLock的实现和tryAcquire的实现基本一致,就是少了一个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");
}
//上述c不等于0的情形也会走到此方法来原子的修改state
if (!compareAndSetState(c, c + 1))
return false;
setExclusiveOwnerThread(current);
return true;
}
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//tryAcquire是子类实现
return tryAcquire(arg) ||
//如果抢占失败,doAcquireNanos将当前线程阻塞直到超时或者获取锁
doAcquireNanos(arg, nanosTimeout);
}
unlock依赖于Sync的tryRelease方法
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
//tryRelease方法是子类实现,返回true表示成功释放锁,返回false表示还不能释放
if (tryRelease(arg)) {
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;
//判断重入的计数是否为0
boolean free = exclusiveCount(nextc) == 0;
if (free)
//计数为0,将占有锁的线程置为null
setExclusiveOwnerThread(null);
//更新state,就是将其置为0
setState(nextc);
return free;
}
lock方法依赖于Sync的tryAcquireShared方法
public void lock() {
sync.acquireShared(1);
}
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
//返回1表示获取读锁成功,-1表示失败,需要阻塞当前线程等待其他线程释放写锁
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
//exclusiveCount不等于0,说明有线程占有了写锁,如果占有写锁的线程不是当前线程则返回-1,必须等待该线程释放写锁
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
//exclusiveCount等于0,说明写锁未被占用或者占用写锁的就是当前线程,可以继续获取读锁
//获取高16位的值,即重入读锁的累计次数
int r = sharedCount(c);
if (!readerShouldBlock() && //readerShouldBlock为false
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) { //原子修改state成功
if (r == 0) {
//保存第一个获取共享锁的线程
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
//如果当前线程就是firstReader,计数加1
firstReaderHoldCount++;
} else {
//如果当前线程不是firstReader
//cachedHoldCounter表示上一次获取共享锁的线程的HoldCounter
HoldCounter rh = cachedHoldCounter;
//第二个获取共享锁的线程进入时,rh就是null,将cachedHoldCounter初始化
//第三个获取共享锁的线程进入时,rh的tid就跟当前线程不一致了,更新cachedHoldCounter
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh); //当某个线程释放了读锁又重新占有读锁,就可能进入此分支
//当count变成0后会从本地线程变量中移除,此处是将其恢复
//增加计数
rh.count++;
}
return 1;
}
//上述if条件为false,比如并发修改state,导致compareAndSetState返回false
return fullTryAcquireShared(current);
}
//fullTryAcquireShared的逻辑和tryAcquireShared大体一致,主要多了一个for循环
final int fullTryAcquireShared(Thread current) {
/*
* This code is in part redundant with that in
* tryAcquireShared but is simpler overall by not
* complicating tryAcquireShared with interactions between
* retries and lazily reading hold counts.
*/
HoldCounter rh = null;
for (;;) {
int c = getState();
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
} else if (readerShouldBlock()) {
// Make sure we're not acquiring read lock reentrantly
if (firstReader == current) {
//正常情况下第一个获取读锁的线程是最后一个释放的,所以如果当前线程就是第一个获取读锁的线程,则认为该线程已经占有读锁
} else {
if (rh == null) {
rh = cachedHoldCounter;
//rh不是当前线程的
if (rh == null || rh.tid != getThreadId(current)) {
//赋值成当前线程的
rh = readHolds.get();
if (rh.count == 0) //rh等于0说明该线程已经释放了占有的读锁,将rh从本地变量表中移除
readHolds.remove();
}
}
//如果count不等于0,说明当前线程还占有读锁,走下面的锁重入逻辑即可
//count等于0说明当前线程已经完全释放了读锁,此时再获取锁,需要进入等待队列
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
cachedHoldCounter = rh; // cache for release
}
return 1;
}//如果compareAndSetState修改失败则继续for循环
}
}
//获取读锁失败,比如其他的某个线程占有了写锁的情形,必须将当前线程阻塞等待写锁释放
//arg在读锁下的值就是1
private void doAcquireShared(int arg) {
//添加一个新的节点到同步链表中,主要有以下两种情形会进入此逻辑
//第一,某个线程占有了写锁,其他线程获取读锁,获取读锁的其他线程就会进入同步链表中,其他获取写锁的线程也会进入等待队列中
//第二,读锁被若干线程占用了,这时某个线程尝试获取写锁,会进入同步链表中,一个新的获取读锁的线程因为readerShouldBlock返回true,也会进入同步链表中
//如果这时又有其他线程尝试获取写锁,也会进入同步链表中,即同步链表中既有获取写锁的也有获取读锁的
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//p可能是获取写锁的节点也可能是获取读锁的节点
final Node p = node.predecessor();
//如果前一个节点是head
if (p == head) {
//尝试获取读锁
int r = tryAcquireShared(arg);
if (r >= 0) {
//获取成功,此时r就是1
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}//如果获取失败则继续苏塞
}
//如果前一个节点不是head,将当前节点对应的线程阻塞,将前一个节点的状态修改成SIGNAL
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
//如果是因为中断被唤醒将interrupted置为true
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
//获取高16位的值
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
//将node置为head
setHead(node);
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
//如果s为null或者是共享节点
if (s == null || s.isShared())
//唤醒下一个共享节点,如此循环直到紧挨着的所有共享节点都被唤醒了
doReleaseShared();
}
}
lockInterruptibly依赖于Sync的tryAcquireShared方法
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
//实现和doAcquireShared一致,就是被中断会抛出异常
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
tryLock依赖于Sync的tryReadLock和tryAcquireShared方法
public boolean tryLock() {
return sync.tryReadLock();
}
final boolean tryReadLock() {
Thread current = Thread.currentThread();
for (;;) {
int c = getState();
//如果是其他线程占有了写锁,此时无法获取读锁,返回false
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return false;
int r = sharedCount(c);
//重入次数达到最大值,抛出异常
if (r == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
//原子的修改state,修改失败则重试,修改成功返回true
if (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 true;
}
}
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
}
//该方法的逻辑和doAcquireShared一致,就是增加了超时时间的判断,如果超时则返回false
private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return true;
}
}
//重新计算剩余时间
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
//剩余时间较长则park,否则通过for循环自旋
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
unlock基于Sync的tryReleaseShared方法
public void unlock() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
//tryReleaseShared尝试释放锁,返回true表示需要释放锁,false表示还有unlock未调用,不能释放
if (tryReleaseShared(arg)) {
//唤醒等待获取读锁的线程
doReleaseShared();
return true;
}
return false;
}
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {
//如果当前线程是第一个获取读锁的线程
if (firstReaderHoldCount == 1)
firstReader = null;
else
//计数减1
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
//如果rh为null 或者不是当前线程的,则从readHolds中获取当前线程对应的HoldCounter
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
//count等于1,再减就变成0,将其从ThreadLocalMap中移除
readHolds.remove();
//count正常不可能小于等于0,如果某个线程的unlock方法调用次数比lock方法多,则会出现此情形,抛出异常
if (count <= 0)
throw unmatchedUnlockException();
}
//将计数减1
--rh.count;
}
//修改state
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
//如果变成0了,则返回true,注意此处是state变成0而不是rh的count变成0
//count变成0只是当前线程不再占有读锁,但是其他线程还在占有读锁
//需要等待所有占有读锁的线程的count都变成,nextc才会变成0,然后释放读锁
return nextc == 0;
}
}
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
//ws等于SIGNAL说明该节点后面还有等待的节点
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; //原子的修改成0,修改失败重试
unparkSuccessor(h); //修改成功唤醒下一个节点对应的线程,被唤醒后就会更改head
}
//ws等于0说明该节点就是最后一个节点了,将其状态置为PROPAGATE,然后退出循环
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // 原子的修改成PROPAGATE,修改失败重试
}
if (h == head) // 如果head改变了则继续循环,否则终止
break;
}
}
这两个的实现如下:
static final class FairSync extends Sync {
private static final long serialVersionUID = -2274990926593161451L;
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
final boolean readerShouldBlock() {
return hasQueuedPredecessors();
}
}
//返回同步链表中是否有待处理的不是当前线程的节点
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
//head的next属性正常不为null
((s = h.next) == null || s.thread != Thread.currentThread());
}
static final class NonfairSync extends Sync {
final boolean writerShouldBlock() {
return false; // writers can always barge
}
final boolean readerShouldBlock() {
return apparentlyFirstQueuedIsExclusive();
}
}
//head的下一个节点是等待获取写锁的节点
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
return (h = head) != null &&
(s = h.next) != null &&
!s.isShared() &&
s.thread != null;
}
对于写锁,公平锁保证只要同步链表中有待处理的节点则将新的获取写锁的节点加入到同步链表的末尾,从而实现最早加入链表中的节点优先获取锁;而非公平锁下,一个新的获取写锁的线程会与准备唤醒的线程竞争锁,如果获取锁则返回,即不用添加到同步链表中。
对于读锁,公平锁的处理跟写锁一直;非公平锁下,只有当同步链表中head的下一个节点是获取写锁对应的节点才返回true,这样可保证如果写锁优先于读锁被获取。
AbstractQueuedSynchronizer维护了两个链表,一个由Node的prev,next属性构成的等待获取锁的同步链表(简称Sync链表),链表头是head,链表尾是tail,head和tail都是AbstractQueuedSynchronizer的实例属性,该链表是一个双向链表,可以通过head和next属性往后遍历,通过tail和prev属性往前遍历;另一个是由Node的nextWaiter属性构成的单向链表(简称Condition链表),该链表的链表头是ConditionObject的firstWaiter属性,链表尾是ConditionObject的lastWaiter属性。
抢占锁失败的时候就会用当前线程初始化一个新的Node节点插入到Sync链表末尾,即插入到原tail节点的后面,新节点作为tail,注意如果链表是空的,即head和tail属性为空,则通过无参构造函数创建一个空的Node节点,将其赋值给head和tail。如果是抢占互斥锁(写锁)则将Node设为EXCLUSIVE模式,nextWaiter属性为null,如果是抢占共享锁(读锁)则Node设为SHARED模式,nextWaiter属性为一个名为SHARED的static final 的Node实例。注意刚加入到Sync链表中的Node节点的waitStatus为初始值0。
当某个锁被释放了,则会唤醒当前head节点的下一个节点简称next节点,如果是公平锁则此时next节点直接获取锁,如果是默认的非公平锁则此时的next节点可能与一个调用lock方法的线程抢占锁,如果抢占失败则next节点继续阻塞,锁释放时会再次唤醒next节点并尝试抢占锁。如果next节点抢占成功,则将next节点置为head节点,thread和prev属性被置为null,原来的head节点的next属性置为null,即彻底从Sync链表中移除了,方便垃圾回收。注意如果next节点是获取读锁的,next节点被置为head节点后,还需要判断下一个节点是否也是获取读锁的,如果是则将其唤醒,同样的被唤醒的获取读锁的线程也会唤醒下一个获取读锁的线程,如此循环直到下一个节点是获取写锁的。这样做是因为获取读锁的节点,不需要等待上一个获取读锁的节点释放锁了才能抢占锁,而是可以直接获取锁。
当在某个ConditionObject实例上调用await方法时,会用当前线程创建一个新的Node节点,并将其插入到lastWaiter的后面,新节点作为lastWaiter,然后将当前线程阻塞,注意此时节点的状态是CONDITION。在同一个ConditionObject实例上调用sinal方法时,会将firstWaiter从Condition链表中移除,firstWaiter的nextWaiter作为新的firstWaiter,然后将原来的firstWaiter节点的状态原子的修改成0,将其插入到Sync链表尾部。插入后如果此时写锁未被占用或者准备释放了,则唤醒该节点对应的线程尝试获取锁,如果抢占失败则继续阻塞,直到抢占成功为止。如果一个因为await方法处于阻塞状态的线程被执行interrupt方法中断了,该线程被唤醒,然后将节点状态原子的修改成0并插入到Sync链表尾部,尝试获取锁,如果抢占失败则继续阻塞。
最后,总结下Node节点的状态变化,如下: