由start函数浅析Java Thread
Java的Thread由创建到实际运行在底层都分别对应着不同主机平台上的线程,如Linux使用pthread_create()函数来创建线程、windows平台使用_beginthreadex()函数来创建线程。下面基于java.lang.Thread.java中的start函数的源码对线程创建及启动进行分析(以hotspot虚拟机为例)。
Java 层面
/src/share/classes/java/lang/Thread.java#l143
public class Thread implements Runnable {
// ...
private long eetop; // 对应VM层面的JavaThread
// ...
public synchronized void start() {
// ...
start0(); // 本地方法
// ...
}
}
start()方法调用了本地方法start0(),查JNI得知调用JVM层面的方法。
/src/share/native/java/lang/Thread.c#l44
{"start0", "()V", (void *)&JVM_StartThread}
即对应VM层面的JVM_StartThread方法。
VM层面
数据结构
java.lang.Thread.java与Vm层面的JavaThread和OSThread共同实现。
/src/share/vm/runtime/thread.hpp#l103
class Thread: public ThreadShadow {
// ...
OSThread* _osthread; // Platform-specific thread information
// ...
}
class JavaThread: public Thread {
// ...
}
VM层面的Thread包含了指向OSThread的指针,OSThread包含了底层平台相关的线程信息,如线程的状态、线程id等。
/src/share/vm/runtime/osThread.hpp#l61
class OSThread: public CHeapObj<mtThread> {
// ...
private:
// _thread_id is kernel thread id (similar to LWP id on Solaris). Each thread has a unique thread_id (BsdThreads or NPTL). It can be used to access /proc.
thread_id_t _thread_id;
}
方法执行
JVM_StartThread
进入JVM_StartThread方法中观察start0()方法执行逻辑。
/src/share/vm/prims/jvm.cpp#l3110
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
// 忽略部分影响流程逻辑分析代码
// ...
JavaThread *native_thread = NULL;
// ...
// 计算线程栈大小
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);
// 线程创建完毕之后处于initialized状态,还未进入runnable状态
// ...
// 线程开始运行
Thread::start(native_thread);
JVM_END
这个方法中包含两个关键函数.
JavaThread(ThreadFunction entry_point, size_t stack_size = 0);
Thread::start(Thread* thread);
1中函数包含了两个参数,第一个参数是线程主函数(可粗犷理解为封装了调用run()的函数地址),第二个参数是线程栈的大小,传入第一个参数的是thread_entry
。
/src/share/vm/prims/jvm.cpp#l3097
static void thread_entry(JavaThread* thread, TRAPS) {
HandleMark hm(THREAD);
Handle obj(THREAD, thread->threadObj());
JavaValue result(T_VOID);
// call_virtual 即方法调用
JavaCalls::call_virtual(&result,
obj,
KlassHandle(THREAD, SystemDictionary::Thread_klass()),
vmSymbols::run_method_name(),
vmSymbols::void_method_signature(),
THREAD);
}
函数体中的vmSymbols::run_method_name()
对应着Thread.java中的run方法。
/src/share/vm/classfile/vmSymbols.hpp#l319
template(run_method_name, "run")
JavaThread::JavaThread
JavaThread构造方法,主要做的包括保存线程的entry_point,将线程创建信息传递至底层os层面。
/src/share/vm/runtime/thread.cpp#l1570
JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
Thread()
// ...
// 保存entry_point
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;
// 根据底层平台调用不同的create_thread()
os::create_thread(this, thr_type, stack_sz);
// The thread is still suspended when we reach here. Thread must be explicit started by creator!
}
os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size)
包含三个参数,分别是
- 线程Thread指针
- 线程类型(区别是否是普通线程或是编译线程)
- 线程栈大小。
os::create_thread
以linux平台为例,create_thread(Thread* thread, ThreadType thr_type, size_t stack_size)
主要包含创建OSThread,使用pthread_create()
创建内核线程,设置线程状态等一系列操作。
/src/os/linux/vm/os_linux.cpp#l850
bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
// Allocate the OSThread object
OSThread* osthread = new OSThread(NULL, NULL);
// ...
osthread->set_thread_type(thr_type);
// Initial state is ALLOCATED but not INITIALIZED
osthread->set_state(ALLOCATED);
// 关联osThread
thread->set_osthread(osthread);
// init thread attributes
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// 栈大小设置相关代码,javathread可用-Xss设置栈大小,必须大于linux的线程栈的最小值,AMD64下最低为64K
// glibc guard page 用于线程栈的保护页,当线程对栈的写入到达guard page时会出现page fault
pthread_attr_setguardsize(&attr, os::Linux::default_guard_size(thr_type));
// ...
pthread_t tid;
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
//...
// Store pthread info into the OSThread
osthread->set_pthread_id(tid);
// Wait until child thread is either initialized or aborted
// 与子线程进行通信,直到子线程状态不是Allocated
{
Monitor* sync_with_child = osthread->startThread_lock();
// 使用了Mutex::_no_safepoint_check_flag通信
MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag);
while ((state = osthread->get_state()) == ALLOCATED) {
// 轮询+wait
sync_with_child->wait(Mutex::_no_safepoint_check_flag);
}
}
// ...
return true;
}
int pthread_create(pthread_t *tidp, const pthread_attr_t *attr, ( void *)(*start_rtn)( void *), void *arg)
包含四个参数。
- 指向线程标识符的指针
- 指向线程属性的指针
- 线程运行函数的起始地址
- 运行函数的参数
这里传入的线程运行函数的起始地址是java_start
, 具体如下。
/src/os/linux/vm/os_linux.cpp#l791
// Thread start routine for all newly created threads
static void *java_start(Thread *thread) {
// 子线程运行函数
// ... cache line相关
OSThread* osthread = thread->osthread();
Monitor* sync = osthread->startThread_lock();
// ...
// thread_id is kernel thread id (similar to Solaris LWP id)
// 设置线程Id
osthread->set_thread_id(os::Linux::gettid());
//... numa相关代码
// 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
{
// 使用Mutex::_no_safepoint_check_flag与父线程通信
MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
// notify parent thread
osthread->set_state(INITIALIZED);
sync->notify_all();
// wait until os::start_thread()
// 轮询+wait,父线程调用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;
}
可以看到,在执行到java_start()的sync->notify_all()
之后,父进程就被唤醒了,至此,JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz)
就执行完毕了,此时会执行JVM_StartThread的Thread::start(native_thread);
这一行指令。
Thread::start
此时子线程已经创建完毕,只不过阻塞等待在INITIALIZED状态上,这时父线程执行Thread::start
。
/src/share/vm/runtime/thread.cpp#l460
void Thread::start(Thread* thread) {
// ...
if (!DisableStartThread) {
if (thread->is_Java_thread()) {
// 在java 层面设置为Runnable状态
java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
java_lang_Thread::RUNNABLE);
}
os::start_thread(thread);
}
}
调用os::start_thread。
/src/share/vm/runtime/os.cpp#l781
void os::start_thread(Thread* thread) {
// guard suspend/resume
MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);
OSThread* osthread = thread->osthread();
osthread->set_state(RUNNABLE);// 到这里osthread状态变为runnble, 只不过子线程在还wait在_no_safepoint_check_flag上
pd_start_thread(thread);
}
调用pd_start_thread,唤醒子线程,准备运行run()函数。
/src/os/linux/vm/os_linux.cpp#l1031
void os::pd_start_thread(Thread* thread) {
OSThread * osthread = thread->osthread();
assert(osthread->get_state() != INITIALIZED, "just checking"); // 此时应该是Runnable
Monitor* sync_with_child = osthread->startThread_lock();
MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag);
// 唤醒子线程
sync_with_child->notify();
}
执行完sync_with_child->notify();
之后,会执行java_start(Thread *thread)的thread->run()
语句,对应JavaThread::run()
方法,具体如下。
void JavaThread::run() {
// 线程相关的一些配置
this->initialize_tlab();
this->record_base_of_stack_pointer();
this->record_stack_base_and_size();
this->initialize_thread_local_storage();
this->create_stack_guard_pages();
this->cache_global_variables();
// ...
thread_main_inner();
}
void JavaThread::thread_main_inner() {
// ...
if (!this->has_pending_exception() &&
!java_lang_Thread::is_stillborn(this->threadObj())) {
// ...
// 调用entry_point,即前文提到的thread_entry&, 也就是java level中Thread的run()函数
this->entry_point()(this, this);
}
DTRACE_THREAD_PROBE(stop, this);
// run()函数执行完成,结束子线程
this->exit(false);
delete this;
}
到这里线程创建的完整过程就分析完了,可以看到线程的在vm层面的状态经历了 Allocated、Initiliazed、Runnable的状态,这过程中还涉及一些安全点(safepoint)、父子线程通信等的细节,调用的流程图如下所示。