博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java线程理解以及openjdk中的实现
阅读量:6631 次
发布时间:2019-06-25

本文共 8364 字,大约阅读时间需要 27 分钟。

  hot3.png

看了《深入理解java虚拟机》java与线程一章提到了linux提供的线程模型是一对一的。我也写过一段linux c,当时开辟多线程也就是调用了pthread_create的库函数。

linux c 线程函数

int pthread_create(pthread_t *tid,const pthread_attr_t *attr,(void*)(*start_rtn)(void*),void *arg);

tid就是线程标识

attr是线程属性

start_rtn是函数指针,就是线程运行的函数

arg是函数的参数

这个函数的运用比较简单,就是把你线程要执行的函数的指针传递给第三个参数,把函数的参数用第四个参数传入。

我的问题也就来了,java的线程执行的内容是写在run方法的,那么c的线程是如何调用java的方法的。最开始我以为是jni做的,就看了看openjdk的源码来验证。

openjdk的实现

java线程是通过start的方法启动执行的,主要内容在native方法start0中。

openjdk的写jni一般是一一对应的,Thread.java对应的就是Thread.c。

static JNINativeMethod methods[] = {    {"start0",           "()V",        (void *)&JVM_StartThread},    {"stop0",            "(" OBJ ")V", (void *)&JVM_StopThread},    {"isAlive",          "()Z",        (void *)&JVM_IsThreadAlive},    {"suspend0",         "()V",        (void *)&JVM_SuspendThread},    {"resume0",          "()V",        (void *)&JVM_ResumeThread},    {"setPriority0",     "(I)V",       (void *)&JVM_SetThreadPriority},    {"yield",            "()V",        (void *)&JVM_Yield},    {"sleep",            "(J)V",       (void *)&JVM_Sleep},    {"currentThread",    "()" THD,     (void *)&JVM_CurrentThread},    {"countStackFrames", "()I",        (void *)&JVM_CountStackFrames},    {"interrupt0",       "()V",        (void *)&JVM_Interrupt},    {"isInterrupted",    "(Z)Z",       (void *)&JVM_IsInterrupted},    {"holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock},    {"getThreads",        "()[" THD,   (void *)&JVM_GetAllThreads},    {"dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads},    {"setNativeName",    "(" STR ")V", (void *)&JVM_SetNativeThreadName},};

start0其实就是JVM_StartThread。此时通过search and replace工具查找JVM_StartThread关键字,在jvm.h中找到了声明,jvm.cpp中有实现。

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))  JVMWrapper("JVM_StartThread");  JavaThread *native_thread = NULL;  bool throw_illegal_thread_state = false;  // We must release the Threads_lock before we can post a jvmti event  // in Thread::start.  {    // Ensure that the C++ Thread and OSThread structures aren't freed before    // we operate.    MutexLocker mu(Threads_lock);    if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {      throw_illegal_thread_state = true;    } else {      jlong size =             java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));      // Allocate the C++ Thread structure and create the native thread.  The      // stack size retrieved from java is signed, but the constructor takes      // size_t (an unsigned type), so avoid passing negative values which would      // result in really large stacks.      size_t sz = size > 0 ? (size_t) size : 0;      native_thread = new JavaThread(&thread_entry, sz);  ……  ……

大概浏览上下文,native_thread的构造应该是在JavaThread的构造中完成的,此处代码就展示到构造JavaThread处。

JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :  Thread()#if INCLUDE_ALL_GCS  , _satb_mark_queue(&_satb_mark_queue_set),  _dirty_card_queue(&_dirty_card_queue_set)#endif // INCLUDE_ALL_GCS{  if (TraceThreadEvents) {    tty->print_cr("creating thread %p", this);  }  initialize();  _jni_attach_state = _not_attaching_via_jni;  set_entry_point(entry_point);  os::ThreadType thr_type = os::java_thread;  thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :                                                     os::java_thread;  os::create_thread(this, thr_type, stack_sz);  _safepoint_visible = false;}

在构造函数中做了几件事,设置entry_point,设置stack_size,create_thread。entry_point在后面讲,这里需要知道此处有这么一个点。我们继续看线程的实现。线程的实现基本是基于系统库函数的(c++11才有的统一线程库)。具体实现在os_linux.cpp中(这里我们只说linux的,其他系统的实现就不在这里了)。

bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {    ……    pthread_t tid;    int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);     ……}

这里终于看到了系统库函数的调用。线程执行的方法是java_start,参数是thread。

// Thread start routine for all newly created threadsstatic void *java_start(Thread *thread) {  // Try to randomize the cache line index of hot stack frames.  // This helps when threads of the same stack traces evict each other's  // cache lines. The threads can be either from the same JVM instance, or  // from different JVM instances. The benefit is especially true for  // processors with hyperthreading technology.  static int counter = 0;  int pid = os::current_process_id();  alloca(((pid ^ counter++) & 7) * 128);  ThreadLocalStorage::set_thread(thread);  OSThread* osthread = thread->osthread();  Monitor* sync = osthread->startThread_lock();  // non floating stack LinuxThreads needs extra check, see above  if (!_thread_safety_check(thread)) {    // notify parent thread    MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);    osthread->set_state(ZOMBIE);    sync->notify_all();    return NULL;  }  // thread_id is kernel thread id (similar to Solaris LWP id)  osthread->set_thread_id(os::Linux::gettid());  if (UseNUMA) {    int lgrp_id = os::numa_get_group_id();    if (lgrp_id != -1) {      thread->set_lgrp_id(lgrp_id);    }  }  // initialize signal mask for this thread  os::Linux::hotspot_sigmask(thread);  // initialize floating point control register  os::Linux::init_thread_fpu_state();  // handshaking with parent thread  {    MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);    // notify parent thread    osthread->set_state(INITIALIZED);    sync->notify_all();    // wait until os::start_thread()    while (osthread->get_state() == INITIALIZED) {      sync->wait(Mutex::_no_safepoint_check_flag);    }  }  // call one more level start routine  thread->run();  return 0;}

这个方法可以直接跳到最后一行,他执行的是传递过来对象的run方法,大家还记得前面构造的JavaThread对象吧。

// The first routine called by a new Java threadvoid JavaThread::run() {  // initialize thread-local alloc buffer related fields  this->initialize_tlab();  // used to test validitity of stack trace backs  this->record_base_of_stack_pointer();  // Record real stack base and size.  this->record_stack_base_and_size();  // Initialize thread local storage; set before calling MutexLocker  this->initialize_thread_local_storage();  this->create_stack_guard_pages();  this->cache_global_variables();  ThreadStateTransition::transition_and_fence(this, _thread_new, _thread_in_vm);  assert(JavaThread::current() == this, "sanity check");  assert(!Thread::current()->owns_locks(), "sanity check");  DTRACE_THREAD_PROBE(start, this);  // This operation might block. We call that after all safepoint checks for a new thread has  // been completed.  this->set_active_handles(JNIHandleBlock::allocate_block());  if (JvmtiExport::should_post_thread_life()) {    JvmtiExport::post_thread_start(this);  }  EventThreadStart event;  if (event.should_commit()) {     event.set_javalangthread(java_lang_Thread::thread_id(this->threadObj()));     event.commit();  }  thread_main_inner();}

这里的代码需要大概过一下,我们看到方法的开头初始化很多信息,例如TLAB,TLS 等。真正执行的代码在thread_main_inner中。

void JavaThread::thread_main_inner() {  assert(JavaThread::current() == this, "sanity check");  assert(this->threadObj() != NULL, "just checking");  // Execute thread entry point unless this thread has a pending exception  // or has been stopped before starting.  // Note: Due to JVM_StopThread we can have pending exceptions already!  if (!this->has_pending_exception() &&      !java_lang_Thread::is_stillborn(this->threadObj())) {    {      ResourceMark rm(this);      this->set_native_thread_name(this->get_thread_name());    }    HandleMark hm(this);    this->entry_point()(this, this);  }  DTRACE_THREAD_PROBE(stop, this);  this->exit(false);  delete this;}

这个方法看到最后都发现没有执行run方法吧,这也是我第一次跟踪代码走完很奇特的地方,完全没说run方法的事情。 this->entry_point()(this, this);其实就是调用run方法的。上面我特意说记着entry_point这个东西。这个在构造JavaThread传入的,是一个名字叫thread_entry的函数。

static void thread_entry(JavaThread* thread, TRAPS) {  HandleMark hm(THREAD);  Handle obj(THREAD, thread->threadObj());  JavaValue result(T_VOID);  JavaCalls::call_virtual(&result,                          obj,                          KlassHandle(THREAD, SystemDictionary::Thread_klass()),                          vmSymbols::run_method_name(),                          vmSymbols::void_method_signature(),                          THREAD);}

里面有JavaCalls的过程,传入的就是 vmSymbols::run_method_name(),就是run方法。具体call_virtual怎么调用的。请参考

总结

jvm在linux中线程的实现就是调用的pthread库,但是线程执行的内容,却是解释执行的(就是上面说的call_virtual的过程),并不是jni的反调。我的理解就是每个c的线程都在解释执行java的run方法,从而达到了java的多线程效果。

转载于:https://my.oschina.net/xpbob/blog/1626775

你可能感兴趣的文章
作为一名合格的JAVA架构师需要点亮哪些技能树?
查看>>
为什么短视频会让人刷不停?背后也许用了这套技术
查看>>
Kubernetes 在知乎上的应用
查看>>
Fescar 发布 0.3.1 版本, 支持 ZooKeeper 注册中心
查看>>
【死磕 Spring】----- IOC 之解析 bean 标签:BeanDefinition
查看>>
4.1 在SELinux中客体类存在的目的
查看>>
基础为重,Python的基础,成就月薪过万
查看>>
PHP浮点数的精确计算BCMath
查看>>
H.264学习笔记之一(层次结构,NAL,SPS)
查看>>
Radware:IP欺诈等让网络攻击难以防范
查看>>
基于Token认证的WebSocket连接
查看>>
【Solidity】2.合约的结构体 - 深入理解Solidity
查看>>
《C语言及程序设计》实践参考——二分法解方程
查看>>
java thread中的wait()和notify()
查看>>
2016最新搜索引擎优化(SEO)重点要素
查看>>
【IOS-COCOS2D-X 游戏开发之二】【必看篇】总结阐述COCOS2D-X与COCOS2D-IPHONE区别;
查看>>
前端面试回忆录 - 滴滴篇 - 凉面
查看>>
jxl导入Excel 切割List 并使用MyBatis批量插入数据库
查看>>
小程序开发总结
查看>>
Tomcat监听器设计思路
查看>>