背景知识
文中参考文章链接:
1、线程创建方式-继承 Thread 类、实现 Runnable 接口、Callable接口 2、openJDK_HotSpot源码下载
前言
在 Java 中,有一句比较流行的话就是万物皆对象,同样的在多线程中,我觉得有一句话也必将贴切, 那就是线程皆Thread。Thread是多线程的根本,在java中,不管是什么方式创建的线程(在上一篇介绍的三种线程创建方式),它的开启都是都是始于Thread的start()方法。
调用start()方法去启动一个线程,当 run 方法中的代码执行完毕以后,线程的生命周期也将终止。通过调用 start()方法的意思是当前线程告诉 JVM,启动调用 start()方法的线程。 下面我们来分析一下启动的原理。
1、java线程创建
1.1 start()
有一些初学者在学习线程的时候会比较疑惑,启动一个线程为什么是调用 start()方法,而不是 run 方法,在此做一个简单的分析,我们先简单看一下 start()方法的定义 :
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* start方法将导致this thread开始执行。由JVM调用this thread的run方法
*
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
*
* 结果是 调用start方法的当前线程 和 执行run方法的另一个线程 同时运行。
*
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*多次启动线程永远不合法。 特别是,线程一旦完成执行就不会重新启动
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
public synchronized void `start()` {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*对于由VM创建/设置的main方法线程或“system”组线程,不会调用此方法。未来添加到此方法的任何新功能可能也必须添加到VM中
*
* A zero status value corresponds to state "NEW".
*status=0 代表是 status 是 "NEW"。
*/
//1、判断线程的转态
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
//2、通知组该线程即将启动,以便将其添加到线程组的列表中;并且减少线程组的未启动线程数递减
group.add(this);
boolean started = false;
try {
//3、调用native方法,底层开启异步线程,并调用run方法
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 */
// 忽略异常。 如果start0抛出一个Throwable,它将被传递给调用堆栈
}
}
}
//native方法,JVM创建并启动线程,并调用run方法
private native void start0();
通过代码需要注意一下几点:
1)start方法用synchronized修饰,为同步方法;
2)虽然为同步方法,但不能避免多次调用问题,用threadStatus来记录线程状态,如果线程被多次 start会抛出异常;threadStatus的状态由JVM控制;
3)使用Runnable时,主线程无法捕获子线程中的异常状态。线程的异常,应在线程内部解决。
1.2 start0()
接着,我们从源码看到调用 start 方法实际上是调用一个 native 方法start0() 来启动一个线程,首先 start0() 这个方法是在Thread 的静态块中来注册的,代码如下 :
public class Thread implements Runnable {
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
}
}
register Natives 的本地方法的定义在文件 Thread.c,它定义了各个操作系统平台要用的关于线程的公共数据和操作,以下是 Thread.c 的全部内容。链接地址:http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/00cd9dc3c2b5/src/share/native/java/lang/Thread.c
static JNINativeMethod methods[] = {
#线程启动调用start0()
{"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},
};
#undef THD
#undef OBJ
#undef STE
JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
从 这 段代码可以看出 , start0() 实 际 会 执 行 JVM_StartThread 方法,这个方法是干吗的呢?从名字上来看,似乎是在 JVM 层面去启动一个线程,如果真的是这样,那么在 JVM 层面,一定会调用 Java 中定义的 run 方法。那接下来继续去找找答案。
2、jvm创建线程
上一节是在java层面使用start()方法启动线程,实际上此时还并未创建线程,线程的创建和启动都是由jvm来调用操作系统的指令进行创建和启动的,接下来我们看一下jvm中是如何创建线程,启动线程的。
2.1 JVM_StartThread
先找到 jvm.cpp 这个文件,这个文件可以在 hotspot 的源码中找到,如果愿意深究源码的,可以参考背景知识中,Hotspot源码下载地址,进行源码下载。
我们接着看下 jvm.cpp中JVM_StartThread方法的定义。
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
......省略........
//1、创建本地线程
native_thread = new JavaThread(&thread_entry, sz);
......省略........
//2、启动线程
Thread::start(native_thread);
JVM_END
JVM_ENTRY 是用来定义JVM_Start Thread函数的,在这个函数里面创建了一个真正和平台有关的本地线程。创建本地线程调用的是native_thread = new JavaThread(&thread_entry, sz);,这里的参数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);
}
其实就是通过回调方法调用Java的线程中定义的run方法,此处是个宏定义,在vmSymbols.hpp文件中可以找到如下代码:
#define VM_SYMBOLS_DO(template, do_alias)
template(run_method_name,"run") //回调的是run方法
这里的接着看new 出来的JavaThread是什么?
2.2 JavaThread
从thread.cpp中找到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);
// Create the native thread itself.
// %note runtime_23
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;
}
此方法有两个参数:
- 1)entry_point:表示函数名称,线程创建成功之后会根据这个函数名称调用对应的函数,也就是run()方法;
- 2)stack_sz:表示当前进程内已经有的线程数量。下面,我们重点关注与一下 os::create_thread这个方法,它实际就是调用平台创建线程的方法,它会根据不同的操作系统去创建线程。
2.3 os::create_thread
上一步的时候,我们说过了,它会根据不同的操作系统调用不同的创建线程的方式,本文就以Linux操作系统为例,打开os_linux.cpp可以找到os::create_thread方法
bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
assert(thread->osthread() == NULL, "caller responsible");
// Allocate the OSThread object
OSThread* osthread = new OSThread(NULL, NULL);
........................
pthread_t tid;
//java_start方法重点看
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
}
2.4 java_start
接着看下·java_start方法:
static void *java_start(Thread *thread) {
。。。。。。。。。。。。。。
// handshaking with parent thread
{
MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
// notify parent thread
//1、设置初始化状态
osthread->set_state(INITIALIZED);
//2、唤醒所有线程
sync->notify_all();
// wait until os::start_thread()
//3、不停的查看线程的当前状态是不是Initialized, 如果是的话,调用了sync->wait()的方法等待。
while (osthread->get_state() == INITIALIZED) {
sync->wait(Mutex::_no_safepoint_check_flag);
}
}
// call one more level start routine
//4、被唤醒后执行run方法
thread->run();
return 0;
}
此方法主要包含几个流程;
1、jvm先设置了当前线程的状态是Initialized;
2、用notify_all()唤醒所有的线程;
3、查看当前线程是不是Initialized状态,如果是的话,调用sync->wait()的方法进行等待;
4、thread->run()要等到被唤醒才能执行。
3、启动线程
3.1 Thread::start
根据2.1节中JVM_StartThread方法定义的,在线程创建之后,就会执行Thread::start(native_thread),用来启动线程,启动线程会调用 Thread.cpp 文件中的Thread::start(Thread* thread)方法。如下:
void Thread::start(Thread* thread) {
trace("start", thread);
// Start is different from resume in that its safety is guaranteed by context or
// being called from a Java method synchronized on the Thread object.
if (!DisableStartThread) {
if (thread->is_Java_thread()) {
// Initialize the thread state to RUNNABLE before starting this thread.
// Can not set it after the thread started because we do not know the
// exact thread state at that time. It could be in MONITOR_WAIT or
// in SLEEPING or some other state.
java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
java_lang_Thread::RUNNABLE);
}
//根据不同的操作系统进行线程的启动
os::start_thread(thread);
}
}
start 方法中会 先判断是否为Java线程,如果是java线程会将线程的状态设置为RUNNABLE,接着调用os::start_thread(thread),调用平台启动线程的方法。
3.2 os::start_thread
在os.cpp中,可以找到os::start_thread方法,
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);
pd_start_thread(thread);
}
该方法设置了线程的状态为RUNNABLE,但没有notify线程,然后又调用了os_linux.cpp中的pd_start_thread(thread):
void os::pd_start_thread(Thread* thread) {
OSThread * osthread = thread->osthread();
assert(osthread->get_state() != INITIALIZED, "just checking");
Monitor* sync_with_child = osthread->startThread_lock();
MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag);
sync_with_child->notify();
}
此时notify了线程,因为这时候的线程的状态是RUNNABLE, 线程被唤醒后,2.4小节中的Java_start方法继续往下执行,于是调用了thread->run()的方法。
4、线程回调
4.1 JavaThread::run()
接着来看一下 Thread.cpp 文件中的 JavaThread::run()方法 。
// The first routine called by a new Java thread
void JavaThread::run() {
// initialize thread-local alloc buffer related fields
this->initialize_tlab();
。。。。。。。。。。。。
// 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方法:
void JavaThread::thread_main_inner() {
assert(JavaThread::current() == this, "sanity check");
assert(this->threadObj() != NULL, "just checking");
.............
//
this->entry_point()(this, this);
}
DTRACE_THREAD_PROBE(stop, this);
this->exit(false);
delete this;
}
方法中调用this->entry_point()(this, this), 在2.2小节中说过entry_point 是一个函数名,线程创建成功后会调用这个函数,这个函数就是在2.1节中定义的run()方法。
#define VM_SYMBOLS_DO(template, do_alias)
template(run_method_name,"run") //回调的是run方法
这也就是为什么在线程启动后,会回调线程里复写的run()方法。
5 小结
线程创建流程图:
以上通过Hotspot源码对线程的创建和启动做了详细的讲解,我们可以清楚的了解到了线程创建和启动的流程和原理,下面对以上过程做一个简单的总结:
- 1)使用new Thread()创建一个线程,然后调用start()方法进行java层面的线程启动;
- 2)调用本地方法start0(),去调用jvm中的JVM_StartThread方法进行线程创建和启动;
- 3)调用new JavaThread(&thread_entry, sz)进行线程的创建,并根据不同的操作系统平台调用对应的os::create_thread方法进行线程创建;
- 4)新创建的线程状态为Initialized,调用了sync->wait()的方法进行等待,等到被唤醒才继续执行thread->run();;
- 5)调用Thread::start(native_thread);方法进行线程启动,此时将线程状态设置为RUNNABLE,接着调用os::start_thread(thread),根据不同的操作系统选择不同的线程启动方式;
- 6)线程启动之后状态设置为RUNNABLE, 并唤醒第4步中等待的线程,接着执行thread->run()的方法;
- 7)JavaThread::run()方法会回调第1步new Thread中复写的run()方法。到此,整个线程的创建和启动流程就完成了。