难度
初级
学习时间
30分钟
适合人群
零基础
开发语言
Java
开发环境
- JDK v11
- IntelliJIDEA v2018.3
友情提示
- 本教学属于系列教学,内容具有连贯性,本章使用到的内容之前教学中都有详细讲解。
- 本章内容针对零基础或基础较差的同学比较友好,可能对于有基础的同学来说很简单,希望大家可以根据自己的实际情况选择继续看完或等待看下一篇文章。谢谢大家的谅解!
1.温故知新
前面在 一章中介绍了 如何判断任意线程是否已持有写锁。通过ReentrantReadWriteLock的isWriteLocked()方法来判断写锁是否已被任意线程持有。
在 一章中介绍了 如何判断锁是否为公平锁isFair()方法。
在 一章中介绍了 如何判断锁是否为公平锁isFair()方法。
在 一章中介绍了 如何使用读写锁实战简易版高并发容器。
在 一章中介绍了 LockSupport的阻塞线程park()方法和唤醒线程unpark()方法 。
现在介绍 LockSupport的设置同步对象park(Object blocker)方法和获取同步对象getBlocker( thread t)方法 。
2.设置同步对象并阻塞当前线程 park(Object blocker)方法
通过上一章 的学习,知道了: 当我们需要阻塞一个线程时,可以调用LockSupport的park()方法 。
但是park()方法不能设置同步对象,若想和隐式锁 synchronized 一样,可以在括号内写上同步对象的话,则必须使用LockSupport类的park(Object blocker)方法。
什么是同步对象?
例如 隐式锁synchronized括号中的对象 :
刚刚说过,我们可以 通过LockSupport类的park(Object blocker)方法使当前线程阻塞的同时还可以设置同步对象 。
park(Object blocker)方法在LockSupport类中的源码:
将注释翻译成中文:
中文注释全文:
设置同步对象并阻塞当前线程,被阻塞的线程可以被unpark(Thread thread)方法唤醒或中断。
去掉注释版:
park(Object blocker)方法作用是设置同步对象并阻塞当前线程,被阻塞的线程可以被unpark(Thread thread)方法唤醒或中断。
访问权限
public :park(Object blocker)方法是公开的。
static :park(Object blocker)方法是静态的。
void :park(Object blocker)方法无返回值。
park(Object blocker)方法只能被类调用。
参数
blocker :负责阻塞此线程的同步对象。
抛出的异常
无。
应用
其实park(Object blocker)方法和park()方法作用是一样的,只不过park(Object blocker)方法可以设置同步对象。
下面,我们就试试park(Object blocker)方法。
首先,创建一个线程并重写run()方法:
然后,在run()方法里面调用LockSupport类的park(Object blocker)方法使当前线程等待:
因为 park(Object blocker)方法需要一个Object类型(任意Java对象)的同步对象 ,所以我们需要创建一个同步对象并传给park(Object blocker)方法:
接着,在线程等待前后各输出一句话,目的是为了看清线程何时被等待以及何时被唤醒:
最后,启动线程:
例子书写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。thread线程被阻塞。
使用LockSupport类的park(Object blocker)方法阻塞的线程可以被中断或被LockSupport类的unpark(Thread thread)方法唤醒 。
下面来看看如何 中断被LockSupport类的park(Object blocker)方法阻塞的线程 。
3.中断线程
接着上一小节的例子写,在启动线程的后面写上中断线程的代码:
然后,在中断线程之前我们先使主线程睡1秒钟:
例子改写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。 阻塞的线程被中断,恢复到运行状态中来 。
除了中断线程,我们还可以使用LockSupport类的unpark(Thread thread)方法进行唤醒。
4.唤醒阻塞线程unpark(Thread thread)方法
unpark(Thread thread)方法在上一章 介绍过,这里就不再赘述了,不清楚的小伙伴请前去查阅。
下面直接使用unpark(Thread thread)方法。
还是上一小节例子。
将中断线程代码换成unpark(Thread thread)方法:
例子改写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。 阻塞的线程被唤醒,恢复到运行状态中来 。
5.获取同步对象 getBlocker(Thread t)方法
我们不光可以通过park(Object blocker)方法设置同步对象,还可以通过getBlocker(Thread t)方法获取指定线程的同步对象 。
getBlocker(Thread t)方法在LockSupport类中的源码:
将注释翻译成中文:
中文注释全文:
返回指定线程的同步对象。如果线程未阻塞,则返回null。
去掉注释版:
getBlocker(Thread t)方法作用是返回指定线程的同步对象。如果线程未阻塞,则返回null。
访问权限
public :getBlocker(Thread t)方法是公开的。
static :getBlocker(Thread t)方法是静态的。
Object :getBlocker(Thread t)方法返回Object类型的值,如果线程未阻塞,则返回null。
getBlocker(Thread t)方法只能被类调用。
参数
t :指定的线程。
抛出的异常
NullPointerException :如果参数为null,则产生此异常。
应用
getBlocker(Thread t)方法需要注意两点:
第一,参数t是否为null。
第二,如果线程未阻塞,方法返回null。
先来看第一点,如果参数t为null,那么getBlocker(Thread t)方法就会抛 NullPointerException 异常。
这一点不用例子演示,直接看getBlocker(Thread t)方法源码即可:
在getBlocker(Thread t)方法内部可以清楚地看见,当传入的线程为null时,抛出 NullPointerException 异常。
再来看第二点,当线程未阻塞时,getBlocker(Thread t)方法返回null。
还是上一小节例子,在thread线程未启动前调用getBlocker(Thread t)方法并输出结果:
然后,在thread线程启动后再次调用getBlocker(Thread t)方法并输出结果:
接着,在unpark()方法调用之后再调用getBlocker(Thread t)方法并输出结果:
最后,我们也在thread线程run()方法内部的等待前后各获取同步对象一次:
例子改写完毕。
运行程序,执行结果:
静图:
从运行结果来看,符合预期。线程在没有被阻塞的时候获取同步对象就是null。
该例子中唯一一次获取到的同步对象不为null的情况是在调用unpark(Thread thread)方法之后:
为什么这个时候获取同步对象不为null?
即以下结果:
因为这个时候thread线程一定被阻塞,即使主线程执行了unpark(Thread thread)方法,thread线程被唤醒也是需要一点时间的,所以当主线程在执行getBlocker(Thread t)方法时恰好可以获取到同步对象。
如果在unpark(Thread thread)方法后面的使当前线程睡1秒钟,再去执行获取同步对象的getBlocker(Thread t)方法,结果就不一样了。
我们来试一下。
改写例子, 在unpark(Thread thread)方法后面添加使当前线程睡1秒钟的代码 :
例子改写完毕。
运行程序,执行结果:
静图:
从运行结果来看,符合预期。
之前能获取到同步对象是因为主线程执行getBlocker(Thread t)方法时,thread线程正在被唤醒但还没醒,现在是我们主线程在执行getBlocker(Thread t)方法前睡了1秒钟,thread线程在这1秒钟之内肯定是醒了。
顺便也说一下, 线程在被等待前后获取到的同步对象都为null :
即这两行代码:
因为线程在被等待前是为阻塞的,线程在被唤醒后也是未阻塞的,所以在这两处获取同步对象,结果都为null 。
最后,我们可以来验证通过getBlocker(Thread t)方法获取到的同步对象就是我们最初设置的那个同步对象。
改写例子, 移除在unpark(Thread thread)方法后面的睡眠线程的代码 :
然后,再在创建同步对象代码的后面输出同步对象:
例子改写完毕。
运行程序,执行结果:
静图:
从运行结果来看,符合预期。
两处对象内存地址完全一致 ,通过getBlocker(Thread t)方法获取到的同步对象就是我们最初设置的那个同步对象。
最后,希望大家可以把这个例子照着写一遍,然后再自己默写一遍,方便以后碰到类似的面试题可以轻松应对。
祝大家编码愉快!
GitHub
本章程序GitHub地址:
总结
- park(Object blocker)方法作用是设置同步对象并阻塞当前线程,被阻塞的线程可以被unpark(Thread thread)方法唤醒或中断。
- getBlocker(Thread t)方法作用是返回指定线程的同步对象。如果线程未阻塞,则返回null。
至此,Java中LockSupport中的park(Object blocker)方法和getBlocker(Thread t)方法相关内容讲解先告一段落,更多内容请持续关注。
答疑
如果大家有问题或想了解更多前沿技术,请在下方留言或评论,我会为大家解答。
上一章
下一章
“全栈2019”Java多线程第五十一章:LockSupport线程等待时间
学习小组
加入同步学习小组,共同交流与进步。
- 方式一:关注头条号Gorhaf,私信“Java学习小组”。
- 方式二:关注公众号Gorhaf,回复“Java学习小组”。
全栈工程师学习计划
关注我们,加入“全栈工程师学习计划”。
版权声明
原创不易,未经允许不得转载!