为创建锁和其他同步类提供线程同步原语.
调用park()时如果许可可用,则直接返回,并消费掉这个许可证,其他情况可能会阻塞.
调用unpark()时如果许可证不可用,则可以使许可证可用(这个许可证和信号量不一样,是不可累加的,最多只有一个许可是可用的.)
park()和unpark()比起已经过时的方法Thread.suspend()和Thead.resume的好处是,不用考虑阻塞任务线程和唤醒任务线程的执行顺序,即:无论park()和unpark()的执行顺序如何,都能保证阻塞任务线程被正常执行.
下面三种情况下park()方法会返回.
- 其他线程对当前线程执行了
unpark() - 其他线程中断了当前线程.
- 没有任何原因的情况下,在任何时间返回.
上面的第三种情况在博客中有说,是因为HotSpot中,对等待条件并没有使用循环判断,所以导致park()有可能在任意时间返回.基于这种情况,所以建议在调用park()外层,使用循环重复检查条件是否满足.类似于:
while (condition) {
LockSupport.park(this);
if (Thread.interrupted()) {
// do something
return;
}
}
// do something
源码查看
类的开头就是下面这段代码, 类加载时,先调用了unsafe.objectFieldOffset()获取Thread类中parkBlocker属性的内存偏移量.
然后在设置Thread实例的parkBlocker属性是使用的是unsafe.putObject().
parkBlocker是用来表示当前等待获取许可证的线程信息的.可以在调用park()时,同时设置它的值,然后在
unpark()线程中调用LockSupport.getBlocker()来获取等待线程信息.
看到这里我就想吐嘈,为啥不直接给Thread的parkBlocker属性添加getter和setter来获取和设置对应的值,
而是要使用Unsafe来做这个事情, 然后就自己YY了下,应该是对于Thread的使用方来说,parkBlocker并不应该
被直接通过Thread获取, parkBlocker就只是在LockSupport中被设值,所以就应该只能通过LockSupport来
进行获取(当然你强行要用反射来拿,我也木有办法).
// Hotspot implementation via intrinsics API
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long parkBlockerOffset;
static {
try {
parkBlockerOffset = unsafe.objectFieldOffset
(java.lang.Thread.class.getDeclaredField("parkBlocker"));
} catch (Exception ex) { throw new Error(ex); }
}
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
unsafe.putObject(t, parkBlockerOffset, arg);
}
代码的其他部分就比较简单了,就是直接调用了Unsafe.park()和Unsafe.unpark()来处理的.
上面提到的那篇博客中,有对HotSpot源码的实现分析, 然而当我看到那些C++代码时,我的表情是这样的:

