难度
初级
学习时间
30分钟
适合人群
零基础
开发语言
Java
开发环境
- JDK v11
- IntelliJIDEA v2018.3
友情提示
- 本教学属于系列教学,内容具有连贯性,本章使用到的内容之前教学中都有详细讲解。
- 本章内容针对零基础或基础较差的同学比较友好,可能对于有基础的同学来说很简单,希望大家可以根据自己的实际情况选择继续看完或等待看下一篇文章。谢谢大家的谅解!
1.温故知新
前面在 一章中介绍了 如何判断锁是否为公平锁isFair()方法。
在 一章中介绍了 如何使用读写锁实战简易版高并发容器。
在 一章中介绍了 LockSupport的阻塞线程park()方法和唤醒线程unpark()方法 。
在 一章中介绍了 LockSupport的设置同步对象park(Object blocker)方法和获取同步对象getBlocker(Thread t)方法 。
在 一章中介绍了 LockSupport的超时自动唤醒阻塞线程parkNanos(long nanos)方法和可设置同步对象的超时自动唤醒阻塞线程parkNanos(Object blocker, long nanos)方法 。
现在介绍 LockSupport的超过截止时间时自动唤醒阻塞线程parkUntil(long deadline)方法和可设置同步对象的超过截止时间时自动唤醒阻塞线程parkUntil(Object blocker, long deadline)方法 。
2.设置线程的等待截止时间 parkUntil(long deadline) 方法
之前在 一章中介绍了park()方法, park()方法可以使当前线程阻塞,但被阻塞的线程只能被中断或被unpark(Thread thread)方法唤醒 。
接下来,为大家介绍一个park()方法的重载方法:parkUntil(long deadline)方法。
parkUntil(long deadline)方法可以设置当线程超过指定等待截止时间时可自动唤醒 。
parkUntil(long deadline)方法在LockSupport类中的源码:
将注释翻译成中文:
中文注释全文:
超过截止时间时自动唤醒阻塞线程,被阻塞的线程也可以被中断或被unpark(Thread thread)方法唤醒。
去掉注释版:
parkUntil(long deadline)方法作用是超过截止时间时自动唤醒阻塞线程,被阻塞的线程也可以被中断或被unpark(Thread thread)方法唤醒 。
访问权限
public :parkUntil(long deadline)方法是公开的。
static :parkUntil(long deadline)方法是静态的。
void :parkUntil(long deadline)方法无返回值。
parkUntil(long deadline)方法只能被类调用。
参数
deadline :指定截止时间(单位:毫秒)。
抛出的异常
无。
应用
其实parkUntil(long deadline)方法和park()方法、park(Object blocker)方法、parkNanos(long nanos)和parkNanos(Object blocker, long nanos)方法作用都一样,都可以是当前线程阻塞。只不过,它们各自可以设置的东西不同。
接下来,我们就来试试parkUntil(long deadline)方法。
首先,创建一个线程并重写run()方法:
接着,在run()方法里面调用parkUntil(long deadline)方法,方法需要传入一个截止时间的毫秒数,我们写3秒行不行呢?先来试试:
然后,在线程被阻塞的前后各输出一句话,目的是为了看清楚线程何时被阻塞的以及何时被唤醒的:
最后,我们启动线程:
例子书写完毕。
运行程序,执行结果:
从运行结果来看,不符合预期。
为什么不符合预期?
线程阻塞还没到3秒钟呢就被唤醒了。截止时间设置肯定是有问题的。
如何解决这个问题?
截止时间应该是未来的时间。
例如,当前时间为:2019-03-28 06:00:00。
如果把截止时间定在未来3秒钟,也就是2019-03-28 06:00:03。
这个时候,截止时间的毫秒数就是2019-03-28 06:00:03它换算之后的值;
而2019-03-28 06:00:03它实际可以看作是2019-03-28 06:00:00换算为毫秒数之后+3000的值,其中3000的单位为毫秒(即3秒钟)。
如上所述,改写例子,获取当前时间的毫秒数:
然后,用当前时间的毫秒数+3000得到截止时间的毫秒数:
例子改写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。 被阻塞的线程在3秒钟之后自动醒来 。
3.更优雅地设置时间Instant类
上面设置时间这块还可以更加优雅,更具有阅读性,那就是使用时间Instant类。
Instant相关知识在之前的 一章中的第4小节介绍过,这里就不再赘述,不清楚的小伙伴请前去查阅。
用Instant.now()方法替代System.currentTimeMillis()方法:
然后,可以继续在后面调用Instant的plusSeconds(long secondsToAdd)方法延长时间(单位:秒):
接着,在plusSeconds(long secondsToAdd)方法后面继续调用toEpochMilli()方法,将当前的截止时间转换为毫秒数并设置到parkUntil(long deadline)方法上:
其实我们可以把这两行代码化为一行代码:
例子改写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。 即使时间写法变了,但是结果依然是正确的,线程被阻塞3秒钟之后醒来 。
4.中断线程
前面说过,parkUntil(long deadline)和其他park()重载方法一样,阻塞的线程可以被中断。
接下来,我们就来试一试。
还是上一小节例子,我们在启动线程的后面写上中断线程代码:
为了让中断线程不要那么快执行,我们让主线程睡1秒钟:
例子改写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。 阻塞的线程在1秒钟之后被中断,从阻塞状态恢复到运行状态中来,继续往下执行 。
5.唤醒阻塞的线程unpark(Thread thread)方法
unpark(Thread thread)方法这里不用再多说,前面几章用过好多次,而且还在 一章中着重介绍过,不清楚的小伙伴可以前去查阅。
接下来,我们就来试试unpark(Thread thread)方法。
还是上一小节的例子,只不过将中断线程的代码替换为unpark(Thread thread)方法:
例子改写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。 阻塞的线程在1秒钟之后被中断,从阻塞状态恢复到运行状态中来,继续往下执行 。
6.设置同步对象和截止时间的parkUntil(Object blocker, long deadline)方法
和上一章中的parkNanos(long nanos)方法提供了一个设置同步对象的parkNanos(Object blocker, long nanos)方法一样,parkUntil(long deadline)也提供了一个可以设置同步对象的parkUntil(Object blocker, long deadline)方法。
显而易见,parkUntil(Object blocker, long deadline)方法是除了阻塞线程基本功能以外,还兼具可设置同步对象和截止时间两个功能的。
同步对象不用多说,我们之前在 一章中介绍过,这里就不再赘述,不清楚的小伙伴可以前去阅读。
接下来,我们就来看一下parkUntil(Object blocker, long deadline)方法吧。
parkUntil(Object blocker, long deadline)方法在LockSupport类中的源码:
将注释翻译成中文:
中文注释全文:
超过截止时间时自动唤醒阻塞线程,被阻塞的线程也可以被中断或被unpark(Thread thread)方法唤醒,方法可设置同步对象。
去掉注释版:
parkUntil(Object blocker, long deadline)方法作用是超过截止时间时自动唤醒阻塞线程,被阻塞的线程也可以被中断或被unpark(Thread thread)方法唤醒,方法可设置同步对象。
访问权限
public :parkUntil(Object blocker, long deadline)方法是公开的。
static :parkUntil(Object blocker, long deadline)方法是静态的。
void :parkUntil(Object blocker, long deadline)方法无返回值。
parkUntil(Object blocker, long deadline)方法只能被类调用。
参数
blocker :同步对象。
deadline :指定截止时间(单位:毫秒)。
抛出的异常
无。
应用
可以看到,parkUntil(Object blocker, long deadline)方法仅仅只是比parkUntil(long deadline)方法多一个可以设置同步对象的方法参数,其他的再无任何区别,所以在用法上,他们俩也没有什么区别。
下面,我们就来试试parkUntil(Object blocker, long deadline)方法。
因为parkUntil(Object blocker, long deadline)方法需要一个同步对象,而这个同步对象可以是任意的Java对象,所以这里我们任意创建一个Java对象作为同步对象:
当同步对象创建出来以后,就可以用parkUntil(Object blocker, long deadline)方法替换掉parkUntil(long deadline)方法,并将同步对象传递给parkUntil(Object blocker, long deadline)方法:
最后,我们将启动线程之后的代码都移除掉,因为这些代码已经不需要了,我们要让线程等待超时自动醒来,所以这些代码可以移除了:
移除之后的代码:
例子改写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。 阻塞的线程在3秒钟之后醒来 。
因为parkUntil(Object blocker, long deadline)方法和parkUntil(long deadline)方法除了一个可以设置同步对象的功能以外,其他功能是一样的,所以parkUntil(Object blocker, long deadline)方法的中断线程和unpark(Thread thread)方法就不再重复演示了,效果是和parkUntil(long deadline)方法一样的。
最后,希望大家可以把这个例子照着写一遍,然后再自己默写一遍,方便以后碰到类似的面试题可以轻松应对。
祝大家编码愉快!
GitHub
本章程序GitHub地址:
总结
- parkUntil(long deadline)方法作用是超过截止时间时自动唤醒阻塞线程,被阻塞的线程也可以被中断或被unpark(Thread thread)方法唤醒。
- parkUntil(Object blocker, long deadline)方法作用是超过截止时间时自动唤醒阻塞线程,被阻塞的线程也可以被中断或被unpark(Thread thread)方法唤醒,方法可设置同步对象。
至此,Java中LockSupport中的parkUntil(long deadline)方法和parkUntil(Object blocker, long deadline)方法相关内容讲解先告一段落,更多内容请持续关注。
答疑
如果大家有问题或想了解更多前沿技术,请在下方留言或评论,我会为大家解答。
上一章
下一章
“全栈2019”Java多线程第五十三章:ThreadLocal简单介绍
学习小组
加入同步学习小组,共同交流与进步。
- 方式一:关注头条号Gorhaf,私信“Java学习小组”。
- 方式二:关注公众号Gorhaf,回复“Java学习小组”。
全栈工程师学习计划
关注我们,加入“全栈工程师学习计划”。
版权声明
原创不易,未经允许不得转载!