• 温故知新-多线程-深入刨析park、unpark




    摘要

    本文主要介绍park、unpark的功能以及hotspot实现原理,为下一篇介绍ReentrantLock做铺垫!

    park、unpark

    LockSupport类是Java6引入的一个类,提供了基本的线程同步原语。LockSupport实际上是调用了Unsafe类里的函数,归结到Unsafe里,两个函数但为上层提供了强大的同步原语。

    public native void unpark(Thread jthread);
    public native void park(boolean isAbsolute, long time);
    // isAbsolute参数是指明时间是绝对的,还是相对的。
    

    unpark函数为线程提供“许可(permit)”,线程调用park函数则等待“许可”。这个有点像信号量,但是这个“许可”是不能叠加的,“许可”是一次性的。

    比如线程B连续调用了三次unpark函数,当线程A调用park函数就使用掉这个“许可”,如果线程A再次调用park,则进入等待状态。

    看一下hotspot实现

    在Parker类里的_counter字段,就是用来记录所谓的“许可”的。

    位置:hotspot/src/share/vm/runtime/park.hpp

    简化代码如下

    class Parker : public os::PlatformParker {
    private:
      volatile int _counter ;
      Parker * FreeNext ;
      JavaThread * AssociatedWith ; // Current association
    public:
      Parker() : PlatformParker() {
        _counter       = 0 ;
        FreeNext       = NULL ;
        AssociatedWith = NULL ;
      }
    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();
    };
    
    • park

      1. 简化代码如下:当调用park时,先尝试直接能否直接拿到“许可”,即_counter>0时,如果成功,则把_counter设置为0,并返回;

      2. 如果不成功,则构造一个ThreadBlockInVM,然后检查_counter是不是>0,如果是,则把_counter设置为0,unlock mutex并返回;

      3. Java dos里提到,当下面三种情况下park函数会返回,在这段代码里也可以体现;

        Some other thread invokes unpark with the current thread as the target; or
        Some other thread interrupts the current thread; or
        The call spuriously (that is, for no reason) returns.

    void Parker::park(bool isAbsolute, jlong time) {
    
      if (Atomic::xchg(0, &_counter) > 0) return;
    
      Thread* thread = Thread::current();
      assert(thread->is_Java_thread(), "Must be JavaThread");
      JavaThread *jt = (JavaThread *)thread;
    
      if (Thread::is_interrupted(thread, false)) {
        return;
      }
      // Next, demultiplex/decode time arguments
      struct timespec absTime;
      if (time < 0 || (isAbsolute && time == 0) ) { // don't wait at all
        return;
      }
      if (time > 0) {
        unpackTime(&absTime, isAbsolute, time);
      }
      ... 
      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") ;
        // Paranoia to ensure our locked and lock-free paths interact
        // correctly with each other and Java-level accesses.
        OrderAccess::fence();
        return;
      }
    
    • unpark

    unpark时,设置_counter为1;如果_counter = 1,unlock mutext返回。如果_counter=0,需要调用pthread_cond_signal唤醒在park中等待的线程:

    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") ;
      }
    }
    

    自此park、unpark已经分析完毕;

    参考

    Java的LockSupport.park()实现分析


    你的鼓励也是我创作的动力

    打赏地址

  • 相关阅读:
    yii2.0 邮件发送如何配置
    php(ThinkPHP)实现微信小程序的登录过程
    微信小程序开发
    一个中高级PHP工程师所应该具备的能力
    如何解决PHP的高并发和大流量的问题
    对于PHP面试知识点的小结
    Centos7 redis设置开机自启动
    CENTOS7下REDIS设置密码、开放远程访问权限
    CentOS7安装Redis
    SQL Server 2012允许远程连接(Windows Server 2016)
  • 原文地址:https://www.cnblogs.com/yangsanchao/p/13062896.html
Copyright © 2020-2023  润新知