失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > LockSupport 以及 park unpark 方法

LockSupport 以及 park unpark 方法

时间:2022-07-18 19:19:07

相关推荐

LockSupport 以及 park unpark 方法

一、LockSupport

是 jsr 166 中新增的 juc 工具类。 LockSupport 类主要用于创建锁和其他同步类来实现线程阻塞。 这个类与他使用的每个线程进行关联, 如果可用就立即 park , 我们可以通过 unpack 方法进行唤醒。

park 方法分析

除非许可证可用,否则出于线程调度目的禁用当前线程。 如果许可证可用,则该许可证被消耗,呼叫立即返回;否则,出于线程调度目的,当前线程将被禁用,并处于休眠状态,直到发生以下三种情况之一:

其他线程以当前线程为目标调用 unpark;或其他线程中断当前线程;或呼叫错误地(即,没有原因地)返回。

此方法不报告导致方法返回的原因。调用方应首先重新检查导致线程停止的条件。调用方还可以在返回时确定线程的中断状态。

public static void park(Object blocker) {Thread t = Thread.currentThread();setBlocker(t, blocker);UNSAFE.park(false, 0L);setBlocker(t, null);}

unpark 方法分析

使给定线程的许可证可用(如果尚未可用)。如果线程在 park上被阻塞,那么它将解除阻塞。否则,它对 park 的下一次呼叫保证不会被阻塞。如果给定的线程尚未启动,则不能保证此操作有任何效果。

public static void unpark(Thread thread) {if (thread != null)UNSAFE.unpark(thread);}

二、JVM 源码分析

park 方法源码分析

UNSAFE_ENTRY(void, Unsafe_Park(JNIEnv *env, jobject unsafe, jboolean isAbsolute, jlong time))UnsafeWrapper("Unsafe_Park");EventThreadPark event;#ifndef USDT2HS_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 USDT2HS_DTRACE_PROBE1(hotspot, thread__park__end, thread->parker());#else /* USDT2 */HOTSPOT_THREAD_PARK_END((uintptr_t) thread->parker());#endif /* USDT2 */if (event.should_commit()) {oop obj = thread->current_park_blocker();event.set_klass((obj != NULL) ? obj->klass() : NULL);event.set_timeout(time);event.set_address((obj != NULL) ? (TYPE_ADDRESS) cast_from_oop<uintptr_t>(obj) : 0);mit();}UNSAFE_ENDUNSAFE_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 samep = (Parker*)addr_from_java(lp);} else {// Grab lock if apparently null or using older version of libraryMutexLocker 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 USDT2HS_DTRACE_PROBE1(hotspot, thread__unpark, p);#else /* USDT2 */HOTSPOT_THREAD_UNPARK((uintptr_t) p);#endif /* USDT2 */p->unpark();}UNSAFE_END

每个线程对象都有一个 Parker 实例:

// JSR166 per-thread parkerprivate:Parker* _parker;public:Parker*parker() { return _parker; }

parker 类的定义如下,我们可以看到:

Parker 类继承 os::PlatformParker。 应该是一个针对不同操作系统适配的;有一个 _counter 属性,可以理解为是否可以调用 park 方法的许可证,只有 _count > 0 的时候才能调用;提供了公开的 park 和 unpark 方法;

class Parker : public os::PlatformParker {private:volatile int _counter ;Parker * FreeNext ;JavaThread * AssociatedWith ; // Current associationpublic:Parker() : PlatformParker() {_counter = 0 ;FreeNext = NULL ;AssociatedWith = NULL ;}protected:~Parker() { ShouldNotReachHere(); }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();// Lifecycle operatorsstatic Parker * Allocate (JavaThread * t) ;static void Release (Parker * e) ;private:static Parker * volatile FreeList ;static volatile int ListLock ;};

前面我提到 Parker 的父类是 PlatformParker (源码文件 os_linux.cpp)。我们可以看下他在 Linunx 下 Parker 的 park 方法的实现过程:

判断是否需要阻塞等待,如果已经是 _counter >0, 不需要等待,将 _counter = 0 , 返回如果 1 不成立,构造当前线程的 ThreadBlockInVM ,检查 _counter > 0 是否成立,成立则将 _counter 设置为 0, unlock mutex 返回;如果 2 不成立,更具需要时间进行不同的函数等待,如果等待正确返回,则将 _counter 设置为0, unlock mutex , park 调用成功。

void Parker::park(bool isAbsolute, jlong time) {// Ideally we'd do something useful while spinning, such// as calling unpackTime().// Optional fast-path check:// Return immediately if a permit is available.// We depend on Atomic::xchg() having full barrier semantics// since we are doing a lock-free update to _counter.if (Atomic::xchg(0, &_counter) > 0) return; // _counter > 0, 使用 xchg 指令修改为 0 返回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 waitif (Thread::is_interrupted(thread, false)) { // 如果线程处于中断状态,直接返回return;}// Next, demultiplex/decode time argumentstimespec absTime;if (time < 0 || (isAbsolute && time == 0) ) { // don't wait at allreturn;}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); // 构造当前线程的 ThreadBlockInVM, 为了防止死锁等特殊场景// Don't wait if cannot get lock since interference arises from// unblocking. Also. check interrupt before trying waitif (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") ;// Paranoia to ensure our locked and lock-free paths interact// correctly with each other and Java-level accesses.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);#endifOSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);jt->set_suspend_equivalent();// cleared by handle_special_suspend_equivalent_condition() or java_suspend_self()assert(_cur_index == -1, "invariant");if (time == 0) {_cur_index = REL_INDEX; // arbitrary choice when not timedstatus = pthread_cond_wait (&_cond[_cur_index], _mutex) ;} else {_cur_index = isAbsolute ? ABS_INDEX : REL_INDEX;// 调用 safe_cond_timedwait 进入线程阻塞status = os::Linux::safe_cond_timedwait (&_cond[_cur_index], _mutex, &absTime) ;if (status != 0 && WorkAroundNPTLTimedWaitHang) {pthread_cond_destroy (&_cond[_cur_index]) ;pthread_cond_init (&_cond[_cur_index], isAbsolute ? NULL : os::Linux::condAttr());}}_cur_index = -1;assert_status(status == 0 || status == EINTR ||status == ETIME || status == ETIMEDOUT,status, "cond_timedwait");#ifdef ASSERTpthread_sigmask(SIG_SETMASK, &oldsigs, NULL);#endif_counter = 0 ; // 返回后 _counter 状态位重置status = pthread_mutex_unlock(_mutex) ;assert_status(status == 0, status, "invariant") ;// Paranoia to ensure our locked and lock-free paths interact// correctly with each other and Java-level accesses.OrderAccess::fence();// If externally suspended while waiting, re-suspendif (jt->handle_special_suspend_equivalent_condition()) {jt->java_suspend_self();}}

unpark 方法源码分析

我们再来看看 unpark (源码文件 os_linux.cpp)。 主要是西安的流程如下:

pthread_mutex_lock 获取锁_counter 设置为 1判断 _counter 的旧值:小于 1 时,调用 pthread_cond_signal 唤醒在 park 阻塞的线程;等于 1 时,直接返回

void Parker::unpark() {int s, status ;status = pthread_mutex_lock(_mutex);assert (status == 0, "invariant") ;s = _counter;_counter = 1;if (s < 1) {// thread might be parkedif (_cur_index != -1) {// thread is definitely parkedif (WorkAroundNPTLTimedWaitHang) {status = pthread_cond_signal (&_cond[_cur_index]);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[_cur_index]);assert (status == 0, "invariant");}} else {pthread_mutex_unlock(_mutex);assert (status == 0, "invariant") ;}} else {pthread_mutex_unlock(_mutex);assert (status == 0, "invariant") ;}}

三、Parker 和 ParkEvent 的区别

ParkEvent 其实和 Parker 的功能类似,同样也可以提供线程阻塞和唤醒的功能。 意思是说ParkEvent是用于java级别的synchronize关键字,Parker是JSR166来的并发工具集合,后面会统一使用ParkEvent。ParkerEvent 继承了PlatformEvent。基类PlatformEvent是特定于平台的,而ParkEvent则是平台无关的。Parker 继承自PlatformParker。

ParkerEvent中的park,unpark方法用于实现Java的object.wait()方法和object.notify()方法;Parker中的park,unpark方法用于实现Java的Locksupprt.park()方法和Locksupprt.unpark()方法;

参考 park.hpp 的源码注释

// The base-class, PlatformEvent, is platform-specific while the ParkEvent is// platform-independent. PlatformEvent provides park(), unpark(), etc., and// is abstract -- that is, a PlatformEvent should never be instantiated except// as part of a ParkEvent.// Equivalently we could have defined a platform-independent base-class that// exported Allocate(), Release(), etc. The platform-specific class would extend// that base-class, adding park(), unpark(), etc.//// A word of caution: The JVM uses 2 very similar constructs:// 1. ParkEvent are used for Java-level "monitor" synchronization.// 2. Parkers are used by JSR166-JUC park-unpark.//// We'll want to eventually merge these redundant facilities and use ParkEvent.

如果觉得《LockSupport 以及 park unpark 方法》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。