一、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);event.commit();}
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 parker
private: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)。 主要是西安的流程如下:

  1. pthread_mutex_lock 获取锁
  2. _counter 设置为 1
  3. 判断 _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。

  1. ParkerEvent中的park,unpark方法用于实现Java的object.wait()方法和object.notify()方法;
  2. 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 方法相关推荐

  1. LockSupport的park/unpark分析

    https://blog.csdn.net/u013978512/article/details/120011860?spm=1001.2014.3001.5501 这篇文章我们讲了AQS的实现过程, ...

  2. LockSupport的park和unpark

    LockSupport是JDK中比较底层的类,用来创建锁和其他同步工具类的基本线程阻塞原语. Java锁和同步器框架的核心AQS:AbstractQueuedSynchronizer,就是通过调用Lo ...

  3. 并发编程之LockSupport的 park 方法及线程中断响应

    系列文章目录 Java并发编程技术知识点梳理(第一篇)操作系统底层工作的整体认识 Java并发编程技术知识点梳理(第二篇)并发编程之JMM&volatile详解 Java并发编程技术知识点梳理 ...

  4. java park unpark_LockSupport(park/unpark)源码分析

    concurrent包是基于AQS (AbstractQueuedSynchronizer)框架的,AQS框架借助于两个类: Unsafe(提供CAS操作) LockSupport(提供park/un ...

  5. Java LockSupport以及park、unpark方法源码深度解析

    介绍了JUC中的LockSupport阻塞工具以及park.unpark方法的底层原理,从Java层面深入至JVM层面. 文章目录 1 LockSupport的概述 2 LockSupport的特征和 ...

  6. java lock park_java并发编程-LockSupport中park与unpark基本使用与原理简单分析

    文章目录 java并发编程原理之---park与unpark 基本使用 情况一,先park再unpark,代码举例与分析 情况二,先unpark再park,代码举例与分析 特点 原理之park &am ...

  7. LockSupport的park和unpark的原理

    我们知道各种并发框架如CountDownLatch.CyclicBarrier和Semaphore是基于AQS (AbstractQueuedSynchronizer)框架实现的,AQS框架借助于两个 ...

  8. park和unpark方法详解

    park和unpark方法详解 基于许可的多线程控制介绍 为了控制多个线程访问共享资源 ,我们需要为每个访问共享区间的线程派发一个许可.拿到一个许可的线程才能进入共享区间活动.当线程完成工作后,离开共 ...

  9. LockSupport 的 park 和 unpark 以及线程中断对 park 的影响

    park() Thread t1 = new Thread(() -> {System.out.println("t1 park");LockSupport.park(); ...

最新文章

  1. 10月份机房技术指标
  2. 就在今晚 | 港科大李世玮教授问诊未来,开辟大湾区新航路
  3. docker开源系统监控软件Nagios
  4. ValidateUtil
  5. 远程仓库---从远程库克隆
  6. java线程怎么用_Java多线程基本使用
  7. HashMap简单源码及多线程下的死循环
  8. Arcgis更换布局模板_小美AI触屏音箱评测 美的布局AloT的关键一环
  9. rk3128屏幕占空比参数设置_rk3128 — spdif dts配置
  10. STL-造汽车的厂家不造轮子
  11. ssh-hostbased配置方法
  12. 数据处理之缺失值处理
  13. 登陆成功率99% 云知声携手平安好医生推声纹登录系统
  14. 再无风清扬,再有少年郎
  15. 1.thrift概述
  16. jenkins调查总结
  17. react中的this.state与this.setState的区别
  18. android编辑图片和文字,微商图片和文字编辑器
  19. 视频压缩编码算法与原理
  20. linux软件源历史版本,解决deepin 15.9.2以后版本软件太旧的问题,混合lion与panda源使用...

热门文章

  1. python枪械_Python的七种武器
  2. TDengine集群搭建
  3. Linux_操作系统-基础操作-教学
  4. 淘宝API接口(item_sku - 获取sku详细信息)
  5. 2012春节回乡见闻
  6. Kaggle word2vec NLP 教程 第三部分:词向量的更多乐趣
  7. 线性代数-克莱姆法则
  8. 【okhttp3.OkHttpClient】ClassNotFoundException: okhttp3.OkHttpClient
  9. Android Studio 开发(二)问题
  10. contiki学习笔记 etimer部分