概述
LockSupport
是用来创建锁和其他同步类的基本线程阻塞原语。
方法列表
1 | // 返回提供给最近一次尚未解除阻塞的 park 方法调用的 blocker 对象,如果该调用不受阻塞,则返回 null。 |
原理
LockSupport
中的park()
和 unpark()
的作用分别是阻塞线程和解除阻塞线程,而且park()
和unpark()
不会遇到Thread.suspend
和 Thread.resume
所可能引发的死锁 问题。
因为park()
和 unpark()
有许可的存在,unpark()
为线程提供“许可(permit)”,线程调用park()
则等待“许可” ;调用 park()
的线程和另一个试图将其 unpark()
的线程之间的竞争将保持活性。
这个有点像信号量,可是这个“许可”是不能叠加的,“许可”是一次性的。
比如:线程B连续调用了三次unpark函数,当线程A调用park函数就使用掉这个“许可”,假设线程A再次调用park,则进入等待状态。
注意。unpark函数能够先于park调用。比方线程B调用unpark函数,给线程A发了一个“许可”,那么当线程A调用park时。它发现已经有“许可”了。那么它会立即再继续执行。
代码示例1
1 | package main.java.com.study.lock.lockSupport; |
执行结果1
1 | main start1 |
结果说明1
总结1
unpark,可以在park之前执行。它们一对一,不管什么时候park只要有unpark过一次就好了。
park被阻塞后,unpark执行后park立即被唤醒。与wait与notify不同,notify唤醒wait后,还必须等待notify代码块中的代码执行完毕后wait才能执行。
代码示例2
1 | package main.java.com.study.lock.lockSupport; |
执行结果2
1 | main start2 |
结果说明2
其实看结果就是线程2阻塞了,线程1唤醒了线程2.就这么简单。
总结
- park()与park(Object obj)有什么区别?
park()
是可以不需要参数的哦,就是说加锁时,不需要指定对象。那么它是怎么加锁的呢?源码如下:
1 | public static void park(Object blocker) { |
可以看出来,阻塞的时候UNSAFE.park(false, 0L)
;根本不需要外面的入参数。
那么setBlocker
是用来干吗的呢?
它是用来做监控用的,当前被阻塞的对象是那个,可以通过LockSupport
中的get
之类的方法来获取当前被阻塞的对象。UNSAFE.park(false, 0L)
;锁住的是当前正在执行的线程。
- 代码示例2中线程2有一个参数,把主线程传过去了, 想用线程2来阻塞主线程。
main中:
1 | Thread thread = Thread.currentThread(); |
线程2中:
1 | LockSupport.park(thread); |
这样是不行的,前面说过park阻塞的是当前正在执行的线程,和外面传递进来的值无关。所以这里被阻塞的还是线程2。
- unpark需要带上参数,这样才知道解锁那个线程。
LockSupport.park 锁住的是Thread.currentThread()当前线程。
Thread.currentThread()与this
类继承了Thread
类后,可以使用this.getName()
;获取当前线程的名称,但是Thread.currentThread().getName()
也获取线程名称。那么它们有什么区别呢?
继承了
Thread
类后使用this.getName()
获取的当前对象的线程名称,而Thread.currentThread().getName()
获取的是当前正在运行的线程的名称。
park和wait的区别
- unpark可以先于park执行,notify不能先于wait执行,不然就不能唤醒了。
- unpark需要指定唤醒的线程,而notify是随机唤醒一个wait。
- unpark解锁后park立即可以执行,notify解锁后wait需要等待notify所在的synchronized代码块执行完毕后wait才能继续执行。
- wait必须在Synchronizer里面才能有效,unpark则不用。
- wait是对象锁,park则是线程锁。
应用示例
1 | package main.java.com.study.lock.lockSupport; |
执行结果: