难度
初级
学习时间
30分钟
适合人群
零基础
开发语言
Java
开发环境
- JDK v11
- IntelliJ IDEA v2018.3
友情提示
- 本教学属于系列教学,内容具有连贯性,本章使用到的内容之前教学中都有详细讲解。
- 本章内容针对零基础或基础较差的同学比较友好,可能对于有基础的同学来说很简单,希望大家可以根据自己的实际情况选择继续看完或等待看下一篇文章。谢谢大家的谅解!
1.温故知新
前面在 一章中介绍了 显式锁Lock的等待唤醒机制。
在 一章中介绍了 Condition对象的await()方法与signal()/signalAll()方法 。
现在我们来讲解 Condition对象的await(long time, TimeUnit unit)方法 。
2.Object与Condition
实际上,当我们用显式锁Lock替代了synchronized时,Object的wait()、notify()和notifyAll()方法都被Condition对象中的方法所替代。
Object的wait()方法被Condition对象分解为以下几个方法:
- void await()
- boolean await(long time, TimeUnit unit)
- long awaitNanos(long nanosTimeout)
- void awaitUninterruptibly()
- boolean awaitUntil(Date deadline)
Object的notify()和notifyAll()方法被Condition对象分解为以下几个方法:
- void signal()
- void signalAll()
以上几个方法后面几章都会讲解。
3.使当前线程等待await()方法
在 一章中介绍了 Condition对象的await()方法。
下面我们简单的回顾一下,然后再延伸出await(long time, TimeUnit unit)方法。
先创建出实现了Runnable接口的匿名内部类对象:

先不着急完善run()方法内容。
接着,创建出3个线程,并将runnable对象传递给构造方法:

然后,启动线程:

这时,我们来完善run()方法内部。
在run()方法内部,需要同步(即获取/释放锁)。既然需要锁,那我们就先创建出显式锁来:

接着,写上获取/释放锁代码:

然后,在获取锁之后使当前线程等待。若想线程等待则需要Condition对象,并调用Condition对象的await()方法。但是在此之前我们需要将Condition对象获取到:

获取到Condition对象之后,再调用其await()方法使当前线程等待:

await()方法会抛出异常,大家别忘了处理,这里我们将其catch:

最后,为了看清线程何时被等待以及何时苏醒,在等待前后各输出一句话:

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

从运行结果来看,符合预期。3个线程都被等待了。
上述结果,除非中断被等待的线程和唤醒它们,否则这些线程醒不来。但是,await()方法有一个 重载 方法await(long time, TimeUnit unit),它可以让线程在一段时间若还取得锁,则自动唤醒。
下面,我们就来看看await(long time, TimeUnit unit)方法。
4.超时自动唤醒 await(long time, TimeUnit unit)方法
await(long time, TimeUnit unit)()方法在Condition接口中的源码:

将注释翻译成中文:

中文注释全文:
造成当前线程在等待,直到它被唤醒,通常由被通知或中断或等待超时。 该方法等同于:awaitNanos(unit.toNanos(time)) > 0
去掉注释版:

await(long time, TimeUnit unit)方法造成当前线程在等待,直到它被唤醒,通常由被通知或中断或等待超时。当线程因超时自动唤醒时方法返回false,被通知唤醒返回true。
访问权限
boolean :await(long time, TimeUnit unit)方法返回boolean类型的值。当线程因超时自动唤醒的返回false,否则返回true。
await(long time, TimeUnit unit)方法只能被对象调用。
参数
long time :等待的最长时间。
TimeUnit unit :时间参数的时间单位。
抛出的异常
throws InterruptedException :如果当前线程被中断(并且支持中断线程挂起)。
应用
下面就将await()方法换成await(long time, TimeUnit unit)方法,需要替换的代码只有以下一处:

替换后:

运行程序,执行结果:

从运行结果来看,线程在等待5秒钟之后自动被唤醒。
但是为什么await(long time, TimeUnit unit)方法返回false?它什么情况下会返回true?

如果线程是调用Condition对象的await(long time, TimeUnit unit)方法而等待,而且因等待超时而被唤醒的,方法返回false,其他情况会不会返回true呢?
其他情况包括哪些情况?
比如中断被等待的线程或主动调用Condition的signal()/signalAll()方法。
下面来先看看中断被等待的线程会怎样。
5.中断被等待的线程
还是上一小节的例子。
我们将主线程睡3秒钟:

然后,让主线程拿到锁:

接着,让主线程中断被等待的所有线程:

例子改写完成。
运行程序,执行结果:

从运行结果来看,中断被等待的线程,不会让线程从等待状态恢复到就运行状态中来,直接是死亡状态。很明显程序执行的catch代码块。
6.唤醒被等待的线程signal()/signalAll()方法
接下来,我们来试试signal()/signalAll()方法。
还是上一小节例子。
将中断线程的代码移除,即以下代码:

移除后,调用Condition对象的signal()/signalAll()方法:

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

从运行结果来看,符合预期。
这次await(long time, TimeUnit unit)方法返回的都是true:

为什么要重载一个返回值是boolean类型的方法?
前提都是线程调用Condition的await(long time, TimeUnit unit)方法而等待的,如果是等待超时的返回false;如果是signal()/signalAll()方法唤醒的返回true。这两个返回值可以给开发人员待线程通过不同方式醒来而做不同的抉择。
当线程等待超时时做什么操作;当线程被signal()/signalAll()方法唤醒时做什么操作。
我们在await(long time, TimeUnit unit)方法注释中看到这一点:

说await(long time, TimeUnit unit)方法等同于awaitNanos(long nanosTimeout) > 0。
下一章,我们就来看看awaitNanos(long nanosTimeout)方法。
最后,希望大家可以把这个例子照着写一遍,然后再自己默写一遍,方便以后碰到类似的面试题可以轻松应对。
祝大家编码愉快!
GitHub
本章程序GitHub地址:
总结
- await()方法使当前线程在等待,直到它被唤醒,通常由被唤醒或中断。
- await(long time, TimeUnit unit)方法造成当前线程在等待,直到它被唤醒,通常由被通知或中断或等待超时。当线程因超时自动唤醒时方法返回false,被通知唤醒返回true。
- signal()方法唤醒正在此对象监视器上等待的单个线程,选择是随机的。
- signalAll()方法作用是唤醒正在此对象监视器上等待的所有线程。
至此,Java中Condition对象的await(long time, TimeUnit unit)方法相关内容讲解先告一段落,更多内容请持续关注。
答疑
如果大家有问题或想了解更多前沿技术,请在下方留言或评论,我会为大家解答。
上一章
下一章
“全栈2019”Java多线程第三十五章:如何获取线程被等待的时间?
学习小组
加入同步学习小组,共同交流与进步。
- 方式一:关注头条号Gorhaf,私信“Java学习小组”。
- 方式二:关注公众号Gorhaf,回复“Java学习小组”。
全栈工程师学习计划
关注我们,加入“全栈工程师学习计划”。

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