前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java中LockSupport.park/unpark源码分析

Java中LockSupport.park/unpark源码分析

作者头像
KINGYT
发布2023-03-15 13:51:48
3470
发布2023-03-15 13:51:48
举报

LockSupport类是java.util.concurrent包中各种锁实现的基础。了解LockSupport的内部机制,对于我们理解concurrent包中的各种锁的实现有很大帮助。

本文将从源码角度分析LockSupport.park/unpark是如何实现的。OpenJDK版本

➜ jdk hg id 76072a077ee1+ jdk-11+28

首先,我们来看下LockSupport.park方法

Java类java.util.concurrent.locks.LockSupport

代码语言:javascript
复制
public static void park() {
    U.park(false, 0L);
}
...
private static final Unsafe U = Unsafe.getUnsafe();

它调用了Unsafe中的park方法,看下该方法

Java类jdk.internal.misc.Unsafe

public native void park(boolean isAbsolute, long time);

该方法是个native方法,看下对应的JVM内部的代码

文件src/hotspot/share/prims/unsafe.cpp

代码语言:javascript
复制
UNSAFE_ENTRY(void, Unsafe_Park(JNIEnv *env, jobject unsafe, jboolean isAbsolute, jlong time)) {
  ...
  thread->parker()->park(isAbsolute != 0, time);
  ...
} UNSAFE_END

该方法先调用了thread->parker()方法,获取到thread专属的parker对象,再调用parker对象的park方法实现最终的park操作。

我们先来看下thread->parker()方法返回的对象的类是什么样子

文件src/hotspot/share/runtime/park.hpp

代码语言:javascript
复制
class Parker : public os::PlatformParker {
private:
  volatile int _counter ;
  ...
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();
  ...
};

该类继承了os::PlatformParker类,我们再看下这个类

文件src/hotspot/os/posix/os_posix.hpp

代码语言:javascript
复制
class PlatformParker : public CHeapObj<mtInternal> {
 protected:
  enum {
    REL_INDEX = 0,
    ABS_INDEX = 1
  };
  int _cur_index;  // which cond is in use: -1, 0, 1
  pthread_mutex_t _mutex[1];
  pthread_cond_t  _cond[2]; // one for relative times and one for absolute
  ...
};

通过上面两个类,我们对Parker对象有了个大概的认识。现在我们继续上面的park方法。

在thread->parker()方法返回Parker对象后,Unsafe_Park方法又调用了其park方法,看下这个方法

文件src/hotspot/os/posix/os_posix.cpp

代码语言:javascript
复制
void Parker::park(bool isAbsolute, jlong time) {
  ...
  // Don't wait if cannot get lock since interference arises from
  // unparking. Also re-check interrupt before trying wait.
  if (Thread::is_interrupted(thread, false) ||
      pthread_mutex_trylock(_mutex) != 0) {
    return;
  }
  ...
  if (_counter > 0)  { // no wait needed
    _counter = 0;
    status = pthread_mutex_unlock(_mutex);
    ...
    return;
  }
  ...
  if (time == 0) {
    _cur_index = REL_INDEX; // arbitrary choice when not timed
    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;


  _counter = 0;
  status = pthread_mutex_unlock(_mutex);
  ...
}

由上面的代码我们可以看到,该方法先调用了pthread_mutex_trylock方法获取锁,然后再判断_counter是否大于0,如果大于0,说明对应的unpark方法已经被调用过,可以直接返回。如果不大于0,则会根据我们传入的time参数,选择对应的pthread_cond_t变量,然后再调用pthread_cond_wait或是pthread_cond_timedwait方法进行等待,直到超时或者收到unpark方法发过来的signal告诉我们返回。

wait方法返回之后,_counter被置为0,表示上次的unpark操作被消耗掉了,如果再调用park方法还是需要等待的。最后unlock,整个方法返回。

至此,整个LockSupport.park方法已经分析完毕。

我们再来看下LockSupport.unpark方法

Java类java.util.concurrent.locks.LockSupport

代码语言:javascript
复制
public static void unpark(Thread thread) {
    if (thread != null)
        U.unpark(thread);
}

该方法同样是调用了Unsafe中的unpark,看下对应方法

Java类jdk.internal.misc.Unsafe

public native void unpark(Object thread);

该方法是个native方法,看下对应的JVM内部的代码

文件src/hotspot/share/prims/unsafe.cpp

代码语言:javascript
复制
UNSAFE_ENTRY(void, Unsafe_Unpark(JNIEnv *env, jobject unsafe, jobject jthread)) {
  Parker* p = NULL;


  if (jthread != NULL) {
    ...
    oop java_thread = NULL;
    ...
    if (java_thread != NULL) {
      ...
      if (lp != 0) {
        ...
        p = (Parker*)addr_from_java(lp);
      } else {
        ...
      }
    }
  }


  if (p != NULL) {
    ...
    p->unpark();
  }
} UNSAFE_END

该方法最终调用了对应Java线程专属的Parker对象的unpark方法,看下这个方法

文件src/hotspot/os/posix/os_posix.cpp

代码语言:javascript
复制
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) {
    // thread is definitely parked
    status = pthread_cond_signal(&_cond[index]);
    ...
  }
}

由上面的代码我们可以看到,该方法首先调用了pthread_mutex_lock方法获取锁,然后记录下当前的_counter和_cur_index值,在解锁之前,_counter也会被设置为1,表示unpark方法已经被调用过。

解锁之后再判断记录的_counter值和_cur_index值,如果_counter小于1则说明unpark方法之前没有被调用过,如果_cur_index不等于-1则说明park方法之前被调用过。

当这两种情况同时成立时,说明对应的Java线程已经处于wait状态,等待我们的unpark操作。所以在此次的unpark方法中,我们先根据_cur_index值,找到对应的pthread_cond_t变量,然后再调用pthread_cond_signal方法,告诉对应的park方法,它可以从wait中返回了。

至此,LockSupport.unpark方法也分析完毕。

综上所述,LockSupport.park/unpark方法本质上是用pthread的mutex and convar机制实现的。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-01-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 卯时卯刻 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档