Jdk1.6 JUC源码解析(5)-locks-LockSupport
作者:大飞
功能简介:
- LockSupport是用于构建锁和其他同步机制的基础类,提供了基本的线程阻塞行为。
源码分析:
- 看下LockSupport代码:
public class LockSupport { private LockSupport() {} // Cannot be instantiated. // 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); } public static Object getBlocker(Thread t) { return unsafe.getObjectVolatile(t, parkBlockerOffset); }
LockSupport中使用Unsafe来做一些阻塞相关的操作,如park、unpark;同时也使用Unsafe来支持对Thread中的parkBlocker域的访问。
看一下LockSupport支持的park操作,park相当于获取可用的许可(初始的许可不可用),调用park()方法会使得当前调用线程阻塞(之前不要调用unpark方法)。
/** * Disables the current thread for thread scheduling purposes unless the * permit is available. * * <p>If the permit is available then it is consumed and the call returns * immediately; otherwise * the current thread becomes disabled for thread scheduling * purposes and lies dormant until one of three things happens: * * <ul> * <li>Some other thread invokes {@link #unpark unpark} with the * current thread as the target; or * * <li>Some other thread {@linkplain Thread#interrupt interrupts} * the current thread; or * * <li>The call spuriously (that is, for no reason) returns. * </ul> * * <p>This method does <em>not</em> report which of these caused the * method to return. Callers should re-check the conditions which caused * the thread to park in the first place. Callers may also determine, * for example, the interrupt status of the thread upon return. * * @param blocker the synchronization object responsible for this * thread parking * @since 1.6 */ public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); unsafe.park(false, 0L); setBlocker(t, null); } public static void parkNanos(Object blocker, long nanos) { if (nanos > 0) { Thread t = Thread.currentThread(); setBlocker(t, blocker); unsafe.park(false, nanos); setBlocker(t, null); } } public static void parkUntil(Object blocker, long deadline) { Thread t = Thread.currentThread(); setBlocker(t, blocker); unsafe.park(true, deadline); setBlocker(t, null); } public static void park() { unsafe.park(false, 0L); } public static void parkNanos(long nanos) { if (nanos > 0) unsafe.park(false, nanos); } public static void parkUntil(long deadline) { unsafe.park(true, deadline); }park系列方法内部都是调用Unsafe的park方法,找一下Unsafe中park方法的实现。找到hotspot/share/vm/prims/unsafe.cpp,可以看到park方法的实现。
{CC"park", CC"(ZJ)V", FN_PTR(Unsafe_Park)}, ... UNSAFE_ENTRY(void, Unsafe_Park(JNIEnv *env, jobject unsafe, jboolean isAbsolute, jlong time)) UnsafeWrapper("Unsafe_Park"); #ifndef USDT2 HS_DTRACE_PROBE3(hotspot, thread__park__begin, thread->parker(), (int) isAbsolute, time); #else /* USDT2 */ HOTSPOT_THREAD_PARK_BEGIN( (uintptr_t) thread->parker(), (int) isAbsolute, time); #endif /* USDT2 */ JavaThreadParkedState jtps(thread, time != 0); thread->parker()->park(isAbsolute != 0, time); #ifndef USDT2 HS_DTRACE_PROBE1(hotspot, thread__park__end, thread->parker()); #else /* USDT2 */ HOTSPOT_THREAD_PARK_END( (uintptr_t) thread->parker()); #endif /* USDT2 */ UNSAFE_END内部调用了线程的parker对象的park方法。找到hotspot/share/vm/runtime/thread.hpp,看到如下代码:
// JSR166 per-thread parker private: Parker* _parker; public: Parker* parker() { return _parker; }继续找到hotspot/share/vm/runtime/park.hpp,看到方法定义:
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);park实现和具体平台相关,找到hotspot/src/os/linux/vm/os_linux.cpp,可以看到方法实现:
void Parker::park(bool isAbsolute, jlong time) { // Optional fast-path check: // Return immediately if a permit is available. if (_counter > 0) { _counter = 0 ; OrderAccess::fence(); return ; } Thread* thread = Thread::current(); assert(thread->is_Java_thread(), "Must be JavaThread"); JavaThread *jt = (JavaThread *)thread; // Optional optimization -- avoid state transitions if there's an interrupt pending. // Check interrupt before trying to wait if (Thread::is_interrupted(thread, false)) { return; } // Next, demultiplex/decode time arguments timespec absTime; if (time < 0 || (isAbsolute && time == 0) ) { // don't wait at all return; } if (time > 0) { unpackTime(&absTime, isAbsolute, time); } // Enter safepoint region // Beware of deadlocks such as 6317397. // The per-thread Parker:: mutex is a classic leaf-lock. // In particular a thread must never block on the Threads_lock while // holding the Parker:: mutex. If safepoints are pending both the // the ThreadBlockInVM() CTOR and DTOR may grab Threads_lock. ThreadBlockInVM tbivm(jt); // Don't wait if cannot get lock since interference arises from // unblocking. Also. check interrupt before trying wait if (Thread::is_interrupted(thread, false) || pthread_mutex_trylock(_mutex) != 0) { return; } int status ; if (_counter > 0) { // no wait needed _counter = 0; status = pthread_mutex_unlock(_mutex); assert (status == 0, "invariant") ; OrderAccess::fence(); return; } #ifdef ASSERT // Don't catch signals while blocked; let the running threads have the signals. // (This allows a debugger to break into the running thread.) sigset_t oldsigs; sigset_t* allowdebug_blocked = os::Linux::allowdebug_blocked_signals(); pthread_sigmask(SIG_BLOCK, allowdebug_blocked, &oldsigs); #endif OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */); jt->set_suspend_equivalent(); // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self() if (time == 0) { status = pthread_cond_wait (_cond, _mutex) ; } else { status = os::Linux::safe_cond_timedwait (_cond, _mutex, &absTime) ; if (status != 0 && WorkAroundNPTLTimedWaitHang) { pthread_cond_destroy (_cond) ; pthread_cond_init (_cond, NULL); } } assert_status(status == 0 || status == EINTR || status == ETIME || status == ETIMEDOUT, status, "cond_timedwait"); #ifdef ASSERT pthread_sigmask(SIG_SETMASK, &oldsigs, NULL); #endif _counter = 0 ; status = pthread_mutex_unlock(_mutex) ; assert_status(status == 0, status, "invariant") ; // If externally suspended while waiting, re-suspend if (jt->handle_special_suspend_equivalent_condition()) { jt->java_suspend_self(); } OrderAccess::fence(); }可见,在linux平台底层用的是POSIX threads API。
接下来看一下unpark系列方法,unpark相当于释放许可(或使许可变为可用)。调用unpark方法会使目标线程在之前阻塞(调用park)地方继续执行,如果目标线程之前没有调用过park,那么在接下来调用park时不会阻塞。
/** * Makes available the permit for the given thread, if it * was not already available. If the thread was blocked on * {@code park} then it will unblock. Otherwise, its next call * to {@code park} is guaranteed not to block. This operation * is not guaranteed to have any effect at all if the given * thread has not been started. * * @param thread the thread to unpark, or {@code null}, in which case * this operation has no effect */ public static void unpark(Thread thread) { if (thread != null) unsafe.unpark(thread); }按照上面类似的过程,找到hotspot/share/vm/prims/unsafe.cpp
UNSAFE_ENTRY(void, Unsafe_Unpark(JNIEnv *env, jobject unsafe, jobject jthread)) UnsafeWrapper("Unsafe_Unpark"); Parker* p = NULL; if (jthread != NULL) { oop java_thread = JNIHandles::resolve_non_null(jthread); if (java_thread != NULL) { jlong lp = java_lang_Thread::park_event(java_thread); if (lp != 0) { // This cast is OK even though the jlong might have been read // non-atomically on 32bit systems, since there, one word will // always be zero anyway and the value set is always the same p = (Parker*)addr_from_java(lp); } 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) { // Bind to Java thread for next time. java_lang_Thread::set_park_event(java_thread, addr_to_java(p)); } } } } } } if (p != NULL) { #ifndef USDT2 HS_DTRACE_PROBE1(hotspot, thread__unpark, p); #else /* USDT2 */ HOTSPOT_THREAD_UNPARK( (uintptr_t) p); #endif /* USDT2 */ p->unpark(); } UNSAFE_END找到hotspot/src/os/linux/vm/os_linux.cpp,可以看到方法实现:
void Parker::unpark() { int s, status ; status = pthread_mutex_lock(_mutex); assert (status == 0, "invariant") ; s = _counter; _counter = 1; if (s < 1) { if (WorkAroundNPTLTimedWaitHang) { status = pthread_cond_signal (_cond) ; assert (status == 0, "invariant") ; status = pthread_mutex_unlock(_mutex); assert (status == 0, "invariant") ; } else { status = pthread_mutex_unlock(_mutex); assert (status == 0, "invariant") ; status = pthread_cond_signal (_cond) ; assert (status == 0, "invariant") ; } } else { pthread_mutex_unlock(_mutex); assert (status == 0, "invariant") ; } }
源码就分析到这里,提几点相关知识:
1.Java线程一般与操作系统进程是一对一的关系,比如在linux平台,对应的是linux的轻量级进程(也就是linux的线程)。
2.linux线程的调度和具体调度器有关,比如CFS调度器下,所有待调度的线程按照nice值排列在一棵红黑树中;系统级的休眠与唤醒依赖系统信号,如果一个线程休眠,会被从红黑树移动到一个等待队列中,被唤醒后再移动回来,大体的过程是这样的。