LockSupport类是java.util.concurrent包中各种锁实现的基础。了解LockSupport的内部机制,对于我们理解concurrent包中的各种锁的实现有很大帮助。
本文将从源码角度分析LockSupport.park/unpark是如何实现的。OpenJDK版本
➜ jdk hg id 76072a077ee1+ jdk-11+28
首先,我们来看下LockSupport.park方法
Java类java.util.concurrent.locks.LockSupport
public static void park() {
U.park(false, 0L);
}
...
private static final Unsafe U = Unsafe.getUnsafe();
它调用了Unsafe中的park方法,看下该方法
Java类jdk.internal.misc.Unsafe
public native void park(boolean isAbsolute, long time);
该方法是个native方法,看下对应的JVM内部的代码
文件src/hotspot/share/prims/unsafe.cpp
UNSAFE_ENTRY(void, Unsafe_Park(JNIEnv *env, jobject unsafe, jboolean isAbsolute, jlong time)) {
...
thread->parker()->park(isAbsolute != 0, time);
...
} UNSAFE_END
该方法先调用了thread->parker()方法,获取到thread专属的parker对象,再调用parker对象的park方法实现最终的park操作。
我们先来看下thread->parker()方法返回的对象的类是什么样子
文件src/hotspot/share/runtime/park.hpp
class Parker : public os::PlatformParker {
private:
volatile int _counter ;
...
public:
// For simplicity of interface with Java, all forms of park (indefinite,
// relative, and absolute) are multiplexed into one call.
void park(bool isAbsolute, jlong time);
void unpark();
...
};
该类继承了os::PlatformParker类,我们再看下这个类
文件src/hotspot/os/posix/os_posix.hpp
class PlatformParker : public CHeapObj<mtInternal> {
protected:
enum {
REL_INDEX = 0,
ABS_INDEX = 1
};
int _cur_index; // which cond is in use: -1, 0, 1
pthread_mutex_t _mutex[1];
pthread_cond_t _cond[2]; // one for relative times and one for absolute
...
};
通过上面两个类,我们对Parker对象有了个大概的认识。现在我们继续上面的park方法。
在thread->parker()方法返回Parker对象后,Unsafe_Park方法又调用了其park方法,看下这个方法
文件src/hotspot/os/posix/os_posix.cpp
void Parker::park(bool isAbsolute, jlong time) {
...
// Don't wait if cannot get lock since interference arises from
// unparking. Also re-check interrupt before trying wait.
if (Thread::is_interrupted(thread, false) ||
pthread_mutex_trylock(_mutex) != 0) {
return;
}
...
if (_counter > 0) { // no wait needed
_counter = 0;
status = pthread_mutex_unlock(_mutex);
...
return;
}
...
if (time == 0) {
_cur_index = REL_INDEX; // arbitrary choice when not timed
status = pthread_cond_wait(&_cond[_cur_index], _mutex);
...
}
else {
_cur_index = isAbsolute ? ABS_INDEX : REL_INDEX;
status = pthread_cond_timedwait(&_cond[_cur_index], _mutex, &absTime);
...
}
_cur_index = -1;
_counter = 0;
status = pthread_mutex_unlock(_mutex);
...
}
由上面的代码我们可以看到,该方法先调用了pthread_mutex_trylock方法获取锁,然后再判断_counter是否大于0,如果大于0,说明对应的unpark方法已经被调用过,可以直接返回。如果不大于0,则会根据我们传入的time参数,选择对应的pthread_cond_t变量,然后再调用pthread_cond_wait或是pthread_cond_timedwait方法进行等待,直到超时或者收到unpark方法发过来的signal告诉我们返回。
wait方法返回之后,_counter被置为0,表示上次的unpark操作被消耗掉了,如果再调用park方法还是需要等待的。最后unlock,整个方法返回。
至此,整个LockSupport.park方法已经分析完毕。
我们再来看下LockSupport.unpark方法
Java类java.util.concurrent.locks.LockSupport
public static void unpark(Thread thread) {
if (thread != null)
U.unpark(thread);
}
该方法同样是调用了Unsafe中的unpark,看下对应方法
Java类jdk.internal.misc.Unsafe
public native void unpark(Object thread);
该方法是个native方法,看下对应的JVM内部的代码
文件src/hotspot/share/prims/unsafe.cpp
UNSAFE_ENTRY(void, Unsafe_Unpark(JNIEnv *env, jobject unsafe, jobject jthread)) {
Parker* p = NULL;
if (jthread != NULL) {
...
oop java_thread = NULL;
...
if (java_thread != NULL) {
...
if (lp != 0) {
...
p = (Parker*)addr_from_java(lp);
} else {
...
}
}
}
if (p != NULL) {
...
p->unpark();
}
} UNSAFE_END
该方法最终调用了对应Java线程专属的Parker对象的unpark方法,看下这个方法
文件src/hotspot/os/posix/os_posix.cpp
void Parker::unpark() {
int status = pthread_mutex_lock(_mutex);
...
const int s = _counter;
_counter = 1;
...
int index = _cur_index;
status = pthread_mutex_unlock(_mutex);
...
if (s < 1 && index != -1) {
// thread is definitely parked
status = pthread_cond_signal(&_cond[index]);
...
}
}
由上面的代码我们可以看到,该方法首先调用了pthread_mutex_lock方法获取锁,然后记录下当前的_counter和_cur_index值,在解锁之前,_counter也会被设置为1,表示unpark方法已经被调用过。
解锁之后再判断记录的_counter值和_cur_index值,如果_counter小于1则说明unpark方法之前没有被调用过,如果_cur_index不等于-1则说明park方法之前被调用过。
当这两种情况同时成立时,说明对应的Java线程已经处于wait状态,等待我们的unpark操作。所以在此次的unpark方法中,我们先根据_cur_index值,找到对应的pthread_cond_t变量,然后再调用pthread_cond_signal方法,告诉对应的park方法,它可以从wait中返回了。
至此,LockSupport.unpark方法也分析完毕。
综上所述,LockSupport.park/unpark方法本质上是用pthread的mutex and convar机制实现的。