难度
初级
学习时间
30分钟
适合人群
零基础
开发语言
Java
开发环境
- JDK v11
- IntelliJIDEA v2018.3
友情提示
- 本教学属于系列教学,内容具有连贯性,本章使用到的内容之前教学中都有详细讲解。
- 本章内容针对零基础或基础较差的同学比较友好,可能对于有基础的同学来说很简单,希望大家可以根据自己的实际情况选择继续看完或等待看下一篇文章。谢谢大家的谅解!
1.温故知新
前面在 一章中介绍了 读写锁ReadWriteLock 。
在 一章中介绍了 读锁与写锁之间相互嵌套情况 。
在 一章中介绍了 可重入读写锁ReentrantReadWriteLock的获取线程与读写锁保持数系列方法 。
在 一章中介绍了 可重入读写锁ReentrantReadWriteLock的查询是否有线程正在等待获取读写锁hasQueuedThreads()方法、获取有多少个正在等待获取读写锁的线程getQueueLength()方法和查询指定线程是否正在等待获取读写锁的hasQueuedThread(Thread thread)方法。
在 一章中介绍了 可重入读写锁ReentrantReadWriteLock与 condition 的关系,在读写锁中,写锁是支持Condition的,读锁是不支持Condition的,原因在于写锁在某一时刻最多只能被一个线程拥有,而读锁在某一时刻最多可以被多个线程拥有,对于读锁而言,其他线程没有必要等待获取读锁,等待唤醒是毫无意义的。
现在介绍 如何查询Condition上是否有线程正在等待和有多少线程正在等待。
2.ReentrantLock与ReentrantReadWriteLock
在开始本章内容之前,我们需要了解ReentrantLock和ReentrantReadWriteLock对Condition的支持情况。
ReentrantLock:可重入锁
ReentrantReadWriteLock:可重入读写锁
ReentrantLock支持Condition。
ReentrantReadWriteLock中,写锁支持Condition,读锁不支持Condition。
注:下面只以ReentrantReadWriteLock中的写锁作为演示对象,ReentrantLock和写锁用法完全相同,这里就不再赘述,如果大家还有疑问请私信我或在评论区留言。
3.查询Condition上是否有线程正在等待hasWaiters(Condition condition)方法
我们可以使用hasWaiters(Condition condition)方法来查询Condition上是否有线程正在等待获取锁。
请问这个方法有什么用呢?
因为我们显式锁Lock可以创建出很多不同用途的Condition,所以查询锁上被等待的线程就必须得按照不同的Condition来查。
下面我们就来看看hasWaiters(Condition condition)方法。
hasWaiters(Condition condition)方法在ReentrantReadWriteLock类中的源码:

将注释翻译成中文:

中文注释全文:
查询Condition上是否有线程正在等待,如果有线程正在Condition上等待,则返回true,否则返回false。
去掉注释版:

hasWaiters(Condition condition)方法作用是查询Condition上是否有线程正在等待,如果有线程正在Condition上等待,则返回true,否则返回false。
访问权限
public :hasWaiters(Condition condition)方法是公开的。
boolean :hasWaiters(Condition condition)方法返回boolean类型的值,如果有线程正在等待获取读锁或写锁则返回true,否则返回false。
hasWaiters(Condition condition)方法只能被对象调用。
参数
condition :Condition对象。
抛出的异常
throws IllegalMonitorStateException: 若没有持有锁时,产生此异常。
throws IllegalArgumentException: 若给定的Condition与锁无关时,产生此异常。
throws NullPointerException: 当给定的Condition对象为null时,产生此异常。
应用
下面我们就来试试hasWaiters(Condition condition)方法。
首先,创建出读写锁:

其次,创建出读锁与写锁:

然后,创建出实现了Runnable接口的匿名内部类,目的是让多个线程共同执行一个任务:

先不着急完善run()方法。
接着,创建出一个线程:

然后,启动线程:

这时我们再来完善run()方法。
在run()方法中获取写锁:

获取到写锁之后再让线程进行等待。
因为涉及到等待,所以需要Condition对象。创建出写锁的Condition对象:

接着,在获取写锁之后调用Condition对象的await()方法让线程进行等待:

接下来,我们该查询写锁Condition对象上是否有线程正在等待。
在哪查询?
就在启动线程之后查询,不过还得让主线程睡1秒钟,目的是让thread1线程有足够的时间获取到写锁,然后进行等待。
让主线程睡1秒钟:

最后,调用读写锁对象的hasWaiters(Condition condition)方法查询写锁Condition对象上是否有线程正在等待:

例子书写完毕。
运行程序,执行结果:

静图:

文字版:
Exception in thread "main" java.lang.IllegalMonitorStateException at java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.hasWaiters(AbstractQueuedSynchronizer.java:2247) at java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.hasWaiters(AbstractQueuedSynchronizer.java:1807) at java.base/java.util.concurrent.locks.ReentrantReadWriteLock.hasWaiters(ReentrantReadWriteLock.java:1433) at main.Main.main(Main.java:62)
从运行结果来看,不符合预期。我们这是出师不利啊。
IllegalMonitorStateException是什么异常?
IllegalMonitorStateException是非法监视状态异常。大多数是因为执行线程没有锁的情况还去执行前置条件是需要锁的操作导致的。
这里则表现在我们主线程在没有持有写锁的情况下还去执行查询写锁Condition对象上是否有线程正在等待操作。
怎么解决这个问题?
解决办法就是 让主线程去获取写锁即可。
在主线程执行hasWaiters(Condition condition)方法之前获取写锁:

例子修改完毕。
运行程序,执行结果:

从运行结果来看,符合预期。写锁Condition对象上的确有线程正在等待。
既然已经知道Condition上有线程正在等待,哪能不能知道有多少各线程正在上面等待?
能, 我们可以通过getWaitQueueLength(Condition condition)方法来获取正在Condition上等待的线程的个数。
4.获取正在Condition上等待的线程的个数getWaitQueueLength(Condition condition)方法
前面我们通过hasWaiters(Condition condition)方法可以得知Condition上是否有正在等待的线程,接下来我们就可以使用getWaitQueueLength(Condition condition)方法来获取在Condition上正在等待的线程的个数。
getWaitQueueLength(Condition condition)方法在ReentrantReadWriteLock类中的源码:

将注释翻译成中文:

中文注释全文:
获取正在Condition上等待的线程的个数。
去掉注释版:

getWaitQueueLength(Condition condition)方法作用是获取正在Condition上等待的线程的个数。
访问权限
public :getWaitQueueLength(Condition condition)方法是公开的。
int :getWaitQueueLength(Condition condition)方法返回int类型的值,方法返回正在Condition上等待的线程的个数。
getWaitQueueLength(Condition condition)方法只能被对象调用。
参数
condition :Condition对象。
抛出的异常
throws IllegalMonitorStateException: 若没有持有锁时,产生此异常。
throws IllegalArgumentException: 若给定的Condition与锁无关时,产生此异常。
throws NullPointerException: 当给定的Condition对象为null时,产生此异常。
应用
下面,我们就来试试getWaitQueueLength(Condition condition)方法。
最好将getWaitQueueLength(Condition condition)方法和hasWaiters(Condition condition)方法搭配在一起使用。
修改主线程在获取写锁之后的代码,先使用hasWaiters(Condition condition)方法判断Condition对象上是否有线程正在等待,然后再使用getWaitQueueLength(Condition condition)方法来获取正在Condition上等待的线程的个数:

例子修改完毕。
运行程序,执行结果:

从运行结果来看,符合预期。的确是有1个线程正在写锁Condition上进行等待。
不如,我们再添加一个线程来再次验证一遍。
修改代码,新建一个线程:

然后,启动线程:

例子修改完毕。
运行程序,执行结果:

从运行结果来看,符合预期。新建的线程加上之前的线程,有2个线程正在写锁Condition上正在等待。
最后,希望大家可以把这个例子照着写一遍,然后再自己默写一遍,方便以后碰到类似的面试题可以轻松应对。
祝大家编码愉快!
GitHub
本章程序GitHub地址:
总结
- hasWaiters(Condition condition)方法作用是查询Condition上是否有线程正在等待,如果有线程正在Condition上等待,则返回true,否则返回false。
- getWaitQueueLength(Condition condition)方法作用是获取正在Condition上等待的线程的个数。
- 最好将getWaitQueueLength(Condition condition)方法和hasWaiters(Condition condition)方法搭配在一起使用。
至此,Java中查询Condition上是否有线程正在被等待和有多少个线程被等待相关内容讲解先告一段落,更多内容请持续关注。
答疑
如果大家有问题或想了解更多前沿技术,请在下方留言或评论,我会为大家解答。
上一章
下一章
学习小组
加入同步学习小组,共同交流与进步。
- 方式一:关注头条号Gorhaf,私信“Java学习小组”。
- 方式二:关注公众号Gorhaf,回复“Java学习小组”。
全栈工程师学习计划
关注我们,加入“全栈工程师学习计划”。

版权声明
原创不易,未经允许不得转载!