上一篇了解了HotSpot关于CAS的支持,作为JUC并发支持,HotSpot提供了Park来支持线程的挂起,唤醒操作。在synchronized层面ObjectMonitor通过ParkEvent来操作线程挂起唤醒,在AQS层面UnSafe通过Parker来操作线程的挂起唤醒。
一.Parker
在JUC中我们考察AQS中的park,以ReentrantLock为例作为入口。
java/util/concurrent/locks/ReentrantLock.java
public class ReentrantLock implements Lock, java.io.Serializable {
//加锁
public void lock() {
sync.acquire(1);
}
//解锁
public void unlock() {
sync.release(1);
}
}
java/util/concurrent/locks/AbstractQueuedSynchronizer.java
AQS用于实现其他各种锁的核心,内部通过链表来管理同步线程,其中线程挂起唤醒借助了UnSafe类
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer{
//尝试获取锁,获取失败则,新增一个独占节点加入到等待队列
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//遍历等待队列
final boolean acquireQueued(final Node node, int arg) {
boolean interrupted = false;
try {
for (;;) {
final Node p = node.predecessor();
//循环队列直到链表头,表头结点获取锁成功,则将node插到表头
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
return interrupted;
}
//获取失败的节点挂起
if (shouldParkAfterFailedAcquire(p, node))
interrupted |= parkAndCheckInterrupt();
}
} catch (Throwable t) {
cancelAcquire(node);
if (interrupted)
selfInterrupt();
throw t;
}
}
//挂起
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
public final boolean release(int arg) {
//尝试释放锁
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
node.compareAndSetWaitStatus(ws, 0);
//循环链表,找到该唤醒的节点,唤醒该节点对应的线程
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node p = tail; p != node && p != null; p = p.prev)
if (p.waitStatus <= 0)
s = p;
}
if (s != null)
//唤醒
LockSupport.unpark(s.thread);
}
}
java/util/concurrent/locks/LockSupport.java
public class LockSupport {
//UnSafe类
private static final Unsafe U = Unsafe.getUnsafe();
//parkBlocker位于Thread.java
private static final long PARKBLOCKER = U.objectFieldOffset
(Thread.class, "parkBlocker");
private static final long SECONDARY = U.objectFieldOffset
(Thread.class, "threadLocalRandomSecondarySeed");
private static final long TID = U.objectFieldOffset
(Thread.class, "tid");
//blocker为AbstractQueuedSynchronizer
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
U.park(false, 0L);
setBlocker(t, null);
}
//设置阻塞对象arg即AbstractQueuedSynchronizer ,PARKBLOCKER 为Thread类中的parkBlocker成员
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
U.putObject(t, PARKBLOCKER, arg);
}
public static void unpark(Thread thread) {
if (thread != null)
U.unpark(thread);
}
}
java/lang/Thread.java
public class Thread implements Runnable {
volatile Object parkBlocker;
}
jdk/internal/misc/Unsafe.java
public final class Unsafe {
@HotSpotIntrinsicCandidate
public native void putObject(Object o, long offset, Object x);
@HotSpotIntrinsicCandidate
public native void park(boolean isAbsolute, long time);
@HotSpotIntrinsicCandidate
public native void unpark(Object thread);
}
src/share/vm/prims/unsafe.cpp
- obj : Thread
- offset: Thread类中的parkBlocker内存偏移
- x_h:AbstractQueuedSynchronizer
UNSAFE_ENTRY(void, Unsafe_PutObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject x_h)) {
oop x = JNIHandles::resolve(x_h); //查找解析AQS的HotSpot内部oop
oop p = JNIHandles::resolve(obj); //查找解析Thread的HotSpot内部oop
//将Thread的parkBlocker设置为AQS
if (UseCompressedOops) {
oop_store((narrowOop*)index_oop_from_field_offset_long(p, offset), x);
} else {
oop_store((oop*)index_oop_from_field_offset_long(p, offset), x);
}
} UNSAFE_END
src/share/vm/prims/unsafe.cpp
- isAbsolute: 是否一直挂起
- time:挂起时间
UNSAFE_ENTRY(void, Unsafe_Park(JNIEnv *env, jobject unsafe, jboolean isAbsolute, jlong time)) {
EventThreadPark event;
HOTSPOT_THREAD_PARK_BEGIN((uintptr_t) thread->parker(), (int) isAbsolute, time);
JavaThreadParkedState jtps(thread, time != 0);
thread->parker()->park(isAbsolute != 0, time); //将当前线程挂起
HOTSPOT_THREAD_PARK_END((uintptr_t) thread->parker());
if (event.should_commit()) {
oop obj = thread->current_park_blocker();
event.set_parkedClass((obj != NULL) ? obj->klass() : NULL);
event.set_timeout(time);
event.set_address((obj != NULL) ? (TYPE_ADDRESS) cast_from_oop<uintptr_t>(obj) : 0);
event.commit();
}
} UNSAFE_END
src/share/vm/prims/unsafe.cpp
jthread为需要唤醒的线程
UNSAFE_ENTRY(void, Unsafe_Unpark(JNIEnv *env, jobject unsafe, jobject jthread)) {
Parker* p = NULL;
if (jthread != NULL) {
//查找解析Thread的HotSpot内部oop
oop java_thread = JNIHandles::resolve_non_null(jthread);
if (java_thread != NULL) {
jlong lp = java_lang_Thread::park_event(java_thread);
if (lp != 0) {
p = (Parker*)addr_from_java(lp); //获取线程对应的Parker
} else {
// Grab lock if apparently null or using older version of library
MutexLocker mu(Threads_lock);
java_thread = JNIHandles::resolve_non_null(jthread);
if (java_thread != NULL) {
JavaThread* thr = java_lang_Thread::thread(java_thread);
if (thr != NULL) {
p = thr->parker();
if (p != NULL) { //绑到线程上,下一次备用,显然这里是为了提高效率
java_lang_Thread::set_park_event(java_thread, addr_to_java(p));
}
}
}
}
}
}
if (p != NULL) {
HOTSPOT_THREAD_UNPARK((uintptr_t) p);
p->unpark(); //唤醒
}
} UNSAFE_END
src/share/vm/runtime/thread.hpp
Parker* parker() { return _parker; }
src/share/vm/runtime/park.hpp
Parker的声明
class Parker : public os::PlatformParker {
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();
}
src/os/linux/vm/os_linux.cpp
Parker的挂起实现
void Parker::park(bool isAbsolute, jlong time) {
//无锁下快速检查_counter
if (Atomic::xchg(0, &_counter) > 0) return;
Thread* thread = Thread::current();
JavaThread *jt = (JavaThread *)thread;
//挂起前检查是否被中断
if (Thread::is_interrupted(thread, false)) {
return;
}
ThreadBlockInVM tbivm(jt);
//再次检查
if (Thread::is_interrupted(thread, false) || pthread_mutex_trylock(_mutex) != 0) {
return;
}
int status;
if (_counter > 0) { // 无需等待
_counter = 0;
status = pthread_mutex_unlock(_mutex);
//lock指令同步内存数据,对所有线程立即可见
OrderAccess::fence();
return;
}
//设置为非Object.wait()
OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
jt->set_suspend_equivalent();
//这里做Linux线程条件挂起
if (time == 0) {
_cur_index = REL_INDEX;
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;
#ifdef ASSERT
pthread_sigmask(SIG_SETMASK, &oldsigs, NULL);
#endif
//线程被唤醒了
_counter = 0;
status = pthread_mutex_unlock(_mutex);
//lock指令同步内存数据,对所有线程立即可见
OrderAccess::fence();
// 设置线程挂起状态
if (jt->handle_special_suspend_equivalent_condition()) {
jt->java_suspend_self();
}
}
src/os/linux/vm/os_linux.cpp
Parker的唤醒实现
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) {
// 唤醒线程
status = pthread_cond_signal(&_cond[index]);
}
}
二.ParkEvent
ParkEvent和synchronized有关,在ObjectMonitor一篇中,线程进入同步块参与锁竞争,没竞争到锁时该线程会被挂起,这个挂起操作是由ParkEvent来实现的。
src/share/vm/runtime/objectMonitor.cpp
void ObjectMonitor::EnterI(TRAPS) {
......
// park self
if (_Responsible == Self || (SyncFlags & 1)) {
TEVENT(Inflated enter - park TIMED);
//挂起指定时间
Self->_ParkEvent->park((jlong) recheckInterval);
// Increase the recheckInterval, but clamp the value.
recheckInterval *= 8;
if (recheckInterval > MAX_RECHECK_INTERVAL) {
recheckInterval = MAX_RECHECK_INTERVAL;
}
} else {
TEVENT(Inflated enter - park UNTIMED);
//挂起
Self->_ParkEvent->park();
}
......
}
上述Self即为当前线程,thread中定义了四种ParkEvent
- _ParkEvent 是给synchronized用的
- _SleepEvent是给线程sleep()用的
- _MutexEvent是给虚拟机内部MutexMonitor用的,这个监视器用于HotSpot内部用的,ObjectMonitor是给Java层用的
- _MuxEvent是个底层Mutex使用
src/share/vm/runtime/thread.hpp
public:
volatile intptr_t _Stalled;
volatile int _TypeTag;
ParkEvent * _ParkEvent; // for synchronized()
ParkEvent * _SleepEvent; // for Thread.sleep
ParkEvent * _MutexEvent; // for native internal Mutex/Monitor
ParkEvent * _MuxEvent; // for low-level muxAcquire-muxRelease
挂起操作_Event状态转变
- -1 => -1 : 违法状态
- 1 => 0 : 不挂起立即返回
- 0 => -1 : 阻塞挂起
src/os/linux/vm/os_linux.cpp
void os::PlatformEvent::park() { // AKA "down()"
int v;
//CAS快速判断
for (;;) {
v = _Event;
if (Atomic::cmpxchg(v-1, &_Event, v) == v) break;
}
if (v == 0) {
// 加锁
int status = pthread_mutex_lock(_mutex);
++_nParked;
while (_Event < 0) {
//阻塞挂起
status = pthread_cond_wait(_cond, _mutex);
if (status == ETIME) { status = EINTR; }
}
--_nParked;
_Event = 0;
status = pthread_mutex_unlock(_mutex);
//lock指令同步内存数据,对所有线程立即可见
OrderAccess::fence();
}
}
src/os/linux/vm/os_linux.cpp
唤醒操作_Event状态转变
- 0 => 1 : 返回,不唤醒
- 1 => 1 : 返回,不唤醒
- -1 => 0 or 1 : 唤醒
void os::PlatformEvent::unpark() {
//无锁快速判断
if (Atomic::xchg(1, &_Event) >= 0) return;
//获取锁定状态
int status = pthread_mutex_lock(_mutex);
int AnyWaiters = _nParked;
status = pthread_mutex_unlock(_mutex);
if (AnyWaiters != 0) {
//信号唤醒
status = pthread_cond_signal(_cond);
}
}
HotSpot内部通过多种手段实现线程的同步和通讯。对于synchronized,HotSpot通过ObjectSynchronizer来实现偏向锁,轻量级锁,重量级锁的管理和Java线程的同步,通过ObjectMonitor和ParkEvent来实现线程的通讯。而对于AQS,HotSpot通过volatile,CAS提供内存模型一致性,通过Parker来协调线程的挂起唤醒。而对于其他诸如Thread.sleep()是通过ParkEvent来实现的。再如已经废弃的yield(),suspend(),resume(),HotSpot通过内部MutexMonitor(非ObjectMonitor)来实现,这里就不在展开了。