在Java中通过如下简单代码就可以创建一个新线程
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
//do something
}
});
thread.start();
在start()中又调用了start0(),它才是真正创建线程的方法。
public synchronized void start() {
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();
start0()是一个native方法,这就表明它具体是由C/C++来实现。要查看源代码,需要下载openjdk。openjdk与常用的oralce jdk相比,前者jdk源码开放的更多。官网地址:OpenJDK
可以选择Mercurial或Github进行下载,此处选择Github
里面提供了很多版本,此处选择jdk8
点击下载即可。
源码下载后,该如何查看呢?针对Thread.java,jdk源码中有一个对应的Thread.c文件
再回到Thread类,在开头有如下一段代码
private static native void registerNatives();
static {
registerNatives();
}
它的作用是在创建Thread类的实例时会先调用registerNatives(),也就是调用Thread.c中的Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)。该方法内部又调用RegisterNatives,将Thread类中的方法与JVM方法进行关联,start0对应的JVM方法就是JVM_StartThread。
JVM_StartThread方法位于hotspot虚拟机里的jvm.cpp,由C++语言编写。
在该方法里会创建JavaThread对象
JavaThread native_thread = new JavaThread(&thread_entry, sz);
在JavaThread构造方法里调用操作系统的创建线程方法
支持如下多个系统
此处选择linux,最终会调用系统函数pthread_create创建线程。这一步就从用户态切换到了内核态,线程创建成功后再从内核态切换到用户态。在Java中不建议频繁创建新线程而是使用线程池做到线程复用,就是因为用户态到内核态的切换会有一定的开销。
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
在Linux中通过命令man pthread_create可查看其用法
注:在CentOS中如果提示No manual entry for pthread_create,需要安装包:yum install man-pages libstdc++-docs
该函数的第三个参数表示在线程创建后执行的方法,此处是java_start。第四个参数表示传入java_start方法的参数,从上下文来看,此处是前面创建的JavaThread对象。
接着看java_start方法,还是在os_linux.cpp中,关键代码如下
static void *java_start(Thread *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;
}
方法里通过osthread->set_state(INITIALIZED)将内核线程状态置为初始状态,接下来while循环直到状态不等于INITIALIZED就会调用JavaThread对象的run方法。那内核线程状态什么时候会改变呢?这又要回到jvm.cpp中的JVM_StartThread方法,在该方法最后会调用如下方法
Thread::start(native_thread);
该方法会调用os::start_thread(thread)
os::start_thread(thread)内部先通过JavaThread对象找到对应的内核线程,然后将内核线程状态置为RUNNABLE。
再次回到上面这张图,while循环条件不满足,终于可以执行下面的thread->run()
static void *java_start(Thread *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;
}
对应thread.cpp里的方法,关键代码如下
void JavaThread::run() {
// We call another function to do the rest so we are sure that the stack addresses used
// from there will be lower than the stack base just computed
thread_main_inner();
}
内部又调用thread_main_inner方法,关键代码this->entry_point()(this, this)
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;
}
entry_point()返回的值为_entry_point,该值在创建JavaThread对象时进行赋值,是thread_entry方法的指针。在该方法中会调用JavaCalls::call_virtual,通过参数run_method_name可以看出最终调用的是Thread类中的run方法。
总结:当调用Thread对象的start方法后,它的内部调用start0方法。接着通过JNI技术调用虚拟机里用C++编写的方法,在该方法中会创建JavaThread对象,在其构造方法中调用系统函数pthread_create创建内核线程。最终在内核线程中执行Thread对象的run方法。