本文出自:【InTheWorld的博客】 (欢迎留言、交流)
前言
之前研究OpenJDK的时候,并没有注意Java线程模型方面的东西。最近在学习一些Java并发方面的知识时,关于JVM线程实现原理的疑问又浮上心头。昨天晚上,突然想起以前研究OpenJDK的资料还在,于是开始在OpenJDK的源码里面研究Thread的实现。搞到凌晨三点多,差不多弄清楚了一个大概,这篇博文就主要把把相关的知识点记录下来。
1. Java中Thread
对于Java开发者来说,java.lang.Thread的确是再熟悉不过了。Java多线程程序都是通过它来实现功能的。有关Thread的使用方式,这里我不想赘述了,而是直奔Thread的实现原理。任何线程的实现都需要内核线程的支持,至少要有一个,不然程序无法执行。用户线程和内核线程的比例(M:N)体现了线程的不同实现方式。而对于高版本的Java(1.3及以后版本)来说,一个Java的Thread就对应一个操作系统的线程,当然少数平台上会有一些区别。
1:1的线程模型其实是非常简单的,带着这个结论,我们通过源码来看看这个1:1模型是如何实现的。首先还是看看java.lang.Thread的源码吧!
public
class Thread implements Runnable {
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
}
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
}
2. Thread的JNI接口
对于一个Java线程来说,start可能是最常用的方法了。通过源码我们可以看到,这个start0()才是真正实现线程启动的函数。然而很不幸,这个一个native函数,它是通过C/C++实现的。事实上,Thread中的大部分重要操作都是native函数完成的,所以,我们需要到OpenJdk里面找到这些函数的实现。所以看看Thread对应的JNI实现,详细代码如下:
/* openjdk\jdk\src\share\native\java\lang\Thread.c */
#include "jni.h"
#include "jvm.h"
#include "java_lang_Thread.h"
#define THD "Ljava/lang/Thread;"
#define OBJ "Ljava/lang/Object;"
#define STE "Ljava/lang/StackTraceElement;"
#define STR "Ljava/lang/String;"
#define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0]))
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},
};
#undef THD
#undef OBJ
#undef STE
#undef STR
JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
JNI的实现很简单,直接暴露的registerNatives()函数实现了多个native函数的注册。了解pthread接口的同学都应该知道,pthread的接口不同于Java Thread,pthread_create()的时候,新线程就启动了。而Java Thread则不同,Java Thread的启动需要调用start()方法,所以可以猜测Java线程对应的native线程其实是在start的时候调用的。补充一句,native线程的实现是平台相关的,这里我选择Linux平台进行分析,Windows虽然没有实现Posix thread接口,但实际上已经和Posix很接近了,所以理解了Linux平台实现,Windows下的实现也可以轻松理解。关于线程启动的start0()方法对应的函数代码如下:
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.
{
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));
size_t sz = size > 0 ? (size_t) size : 0;
native_thread = new JavaThread(&thread_entry, sz);
if (native_thread->osthread() != NULL) {
// Note: the current thread is not being used within "prepare".
native_thread->prepare(jthread);
}
}
}
if (throw_illegal_thread_state) {
THROW(vmSymbols::java_lang_IllegalThreadStateException());
}
Thread::start(native_thread);
JVM_END
第三行代码定义了一个JavaThread类型的native_thread指针,这个变量就是Java thread对应的native线程数据结构。对于JavaThread这个C++类的具体实现,稍后再分析。首先看看,JavaThread和Java的Thread是如何联系起来的?答案就是native_thread->prepare(jthread)这行代码。这个函数的源码如下,非常简单。
set_threadObj(thread_oop());
java_lang_Thread::set_thread(thread_oop(), this);
第一行是把Java的Thread信息记录到JavaThread对象中,第二行则是把JavaThread对象记录到Java的Thread对象中。从此,Java线程和C++线程就联系起来了。
3. C++的JavaThread
C++的JavaThread提供线程的实质性操作。JavaThread是Thread的子类,C++ Thread类还有其他一些子类,这里暂且按下不表。JavaThread是一个比较复杂的类,毕竟它实现了很多的功能。这里就简要介绍一下JavaThread的构造和启动,JavaThread的其中一个重要构造函数如下:
JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
Thread()
{
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;
}
os::create_thread()实现了实际的线程创建的工作。create_thread()函数的主干代码如下:
bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
OSThread* osthread = new OSThread(NULL, NULL);
if (osthread == NULL) {
return false;
}
osthread->set_thread_type(thr_type);
// Initial state is ALLOCATED but not INITIALIZED
osthread->set_state(ALLOCATED);
thread->set_osthread(osthread);
// init thread attributes
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
ThreadState state;
{
bool lock = os::Linux::is_LinuxThreads() && !os::Linux::is_floating_stack();
if (lock) {
os::Linux::createThread_lock()->lock_without_safepoint_check();
}
pthread_t tid;
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
pthread_attr_destroy(&attr);
}
return true;
}
pthread_create(&tid, &attr, (void* ()(void)) java_start, thread)语句中的java_start是JavaThread的routing函数。这个函数中会等待一个信号量,在获得信号量后,它会调用JavaThread对应的java.lang.Thread对象的run()方法。而这个信号量是通过Thread::start(native_thread)这个函数发送的。
到这里JavaThread的构造和启动就基本了解各大概了。这篇《JVM原理与实现——Thread》就先到这里了。欢迎留言、交流、讨论!