在使用JUC中的锁时(如ReentrantLock、ReentrantReadWriteLock),底层维护了两个队列(同步队列和等待队列),但是并没有提供相关的API来获取其队列的长度。我这里通过反射写了一个工具类,可以传入一个Lock实例获取两个队列的长度
在写这个工具类前,需要先完全弄清楚AQS与Lock的结构(包括其中的内部类、内部类子类)。尤其是不能忽略实际上Lock在运行中中创建的Sync对象是其子类NonfairSync和fairSync。如果需要反射获取到AQS中的Node节点,就需要连续两次syncClazz.getSuperclass().getSuperclass()获取父类
下面标题1给出最终工具类,标题2开始分析源码和改造思路,按照这个思路还可以自由地更改AQS锁队列的顺序,指定剔除等
获取同步队列长度:传入Lock实例
获取等待队列长度:传入Condition实例
public class AQSUtils {
/**
* 获取同步队列长度(线程lock被阻塞 + 正持有锁的 节同步队列点总数) 支持JUC下locks包中所有锁
*
* @Author: zjh
* @Date: 2022/8/23 10:14
*/
public static int getSyncQueueLength(Lock lock) throws NoSuchFieldException, IllegalAccessException {
Class extends Lock> lockClazz = lock.getClass();
//先获取内部类Sync实例,因为Sync继承于AbstractQueuedSynchronizer,从而可以获得Node head节点
//这里Lock接口下的几个实现类(如ReentrantLock、ReentantReadWriteLock)中Sync都是内部类
Field syncField = lockClazz.getDeclaredField("sync");
syncField.setAccessible(true);
AbstractQueuedSynchronizer sync = (AbstractQueuedSynchronizer) syncField.get(lock);
Class extends AbstractQueuedSynchronizer> syncClazz = sync.getClass();
//***十分需要注意,这里用了两次getSuperclass()
Class AQSClazz = (Class) syncClazz.getSuperclass().getSuperclass();
//获取同步队列的头节点 末尾节点
Field headNode = AQSClazz.getDeclaredField("head");
headNode.setAccessible(true);
//这里实际上是同步队列的Node节点,碍于访问修饰符问题,只能通过反射获取next节点的值
Object head = headNode.get(sync);
//计算长度
int length = 0;
Object temp = head;
while (temp != null) {
length++;
Class> nodecLazz = temp.getClass();
Field nextNode = nodecLazz.getDeclaredField("next");
nextNode.setAccessible(true);
temp = nextNode.get(temp);
}
return length;
}
/**
* 每个Condition对象中都持有一个等待队列,获取其等待队列长度
*
* @Author: zjh
* @Date: 2022/8/23 10:18
*/
public static int getWaiteQueueLengthInCondition(Condition condition)
throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
Class extends Condition> conClazz = condition.getClass();
Field firstWaiterNode = conClazz.getDeclaredField("firstWaiter");
firstWaiterNode.setAccessible(true);
Object firstWaiter = firstWaiterNode.get(condition);
int length = 0;
Object temp = firstWaiter;
while(temp!=null){
length++;
Class> nodeClazz = temp.getClass();
Field next = nodeClazz.getDeclaredField("nextWaiter");
next.setAccessible(true);
temp = next.get(temp);
}
return length;
}
}
在main中创建Lock和Condition实例:
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
创建3个这样的线程:
//lock加锁后await阻塞————》同步队列不变 等待队列+1
new Thread(()->{
lock.lock();
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.unlock();
}).start();
创建5个这样的线程:
//lock加锁 同步队列+1 等待队列不变
new Thread(()->{
lock.lock();
}).start();
调用AQSUtils
TimeUnit.SECONDS.sleep(1);//保证线程全部开启
int syncQueueLength = AQSUtils.getSyncQueueLength(lock);
System.out.println(syncQueueLength);
int waiteQueueLengthInCondition = AQSUtils.getWaiteQueueLengthInCondition(condition);
System.out.println(waiteQueueLengthInCondition);
结果:同步队列5个 等待队列3个

同理,调用signal()也会让等待队列-1 同步队列+1,signalAll()会将对应condition中所有等待队列节点全部移入同步队列
这个其实主要是看AQS源码的结构,已知:
在main线程中运行如下代码,debug可知产生了同步队列的节点
ReentrantLock lock = new ReentrantLock(false);
//用于通知生产者进行生产
Condition pro = lock.newCondition();
new Thread(()->{
//产生同步队列
lock.lock();
}).start();
new Thread(()->{
//产生同步队列
lock.lock();
}).start();
new Thread(()->{
//产生同步队列
lock.lock();
}).start();
Sync实例的直接Node属性是同步队列,Sync.



Lock——》Sync——》获取父类的父类AQS——》获取head节点——》循环遍历next节点
Lock——》生成的Condition——》获取firstWaiter节点——》循环遍历nextWaiter节点