我们知道java中执行一个线程分为两步:创建Thread和启动start
第一步:创建线程对象。继承Thread对象重写run方法,或者实现Runnable接口run方法并通过Thread的构造方法创建Thread对象,
第二步:调用Thread的start方法启动线程
按照上面两步操作后,就可以开启线程来执行任务了,如下:
package cn.ckeen.threadtest;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @description:线程创建方法
* @author: CKeen
* @date: 2022/5/16 10:05 上午
*/
public class MainThread extends Thread {
public static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) {
// 方式一: 继承Thread类,重写run方法
Thread thread = new MainThread();
thread.start();
// 实现runnable接口
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
doWork();
}
});
thread2.start();
}
@Override
public void run() {
doWork();
}
static void doWork(){
while(count.get() < 1000){
int countVal = count.getAndIncrement();
System.out.println("Hello World! count:" + String.valueOf(countVal));
try {
Thread.sleep(1000L);
}catch (Exception ex){
ex.printStackTrace();
}
}
}
}
看到这里我们一定会很好奇,Thread创建和执行的过程是怎么样的呢?
下面我们就来一步一步跟踪Thread的创建和执行过程:
第一步,我们看下Thread对象的创建,我们通过Thread类跳转到JDK源码可以看到有以下几个构造函数
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
Thread(Runnable target, AccessControlContext acc) {
init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
}
public Thread(ThreadGroup group, Runnable target) {
init(group, target, "Thread-" + nextThreadNum(), 0);
}
public Thread(String name) {
init(null, null, name, 0);
}
public Thread(ThreadGroup group, String name) {
init(group, null, name, 0);
}
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name) {
init(group, target, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
init(group, target, name, stackSize);
}
这里实际干活的是Thread的init方法,我们继续跟到init
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
if (security != null) {
g = security.getThreadGroup();
}
if (g == null) {
g = parent.getThreadGroup();
}
}
g.checkAccess();
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
this.stackSize = stackSize;
tid = nextThreadID();
}
init方法实际只是初始化了Thread的一个参数,比如thread的分组group,target设置,堆栈大小,tid等,到这里Thread对象就已经创建完成
第二步,我们继续看一下Thread对象的启动方法start:
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) {
}
}
}
private native void start0();
跳转到start方法,可以看到start方法调用了start0,而start0方法是个native方法,实际干活的虚拟机对外提供的native的方法了。
为了进一步搞清楚Thread是怎么启动的,必须得知道start0方法都干了些什么,所以我们要跟踪到JVM的实现部分源码。这里我们基于openjdk版本jdk8-b120版本的进行研究,具体的源码可以自己去github的openjdk拉取。
下面我们具体来分析一下JDK对start0实现:
1. 打开源码目录,首先我们可以看到代码目录结构
我们只分析源码部分,不实际编译实际JDK的代码,所以我们主要关注两个以下两个目录:
- jni函数映射目录jdk/src/share/native/java/,该目录下有jdk的native函数到JVM函数的映射定义。
- jvm实现目录hotspot/src/share/vm, 等一会我们会涉及到该目录下的prims目录和runtime目录。
2. 我们找到Thread类的中native函数映射文件:thread.c(源码详细路径:jdk/share/native/java/lang/)
// jdk/share/native/java/lang/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的native方法映射到了JVM的JVM_StartThread方法,我们继续看一下该方法的实现
2. 我们查到JVM_StartThread方法,可以在jvm.h的头文件中找到JVM_StartThread方法的定义,如下:
// hotspot的虚拟机实现:hotspot/src/share/vm/prims/jvm.h
JNIEXPORT void JNICALL
JVM_StartThread(JNIEnv *env, jobject thread);
进一步我们可以找到jvm.cpp代码的对JVM_StartThread方法的实现,如下:
// hotspot/src/share/vm/prims/jvm.cpp
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
bool throw_illegal_thread_state = false;
{
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));
native_thread = new JavaThread(&thread_entry, sz);
if (native_thread->osthread() != NULL) {
native_thread->prepare(jthread);
}
}
}
if (throw_illegal_thread_state) {
THROW(vmSymbols::java_lang_IllegalThreadStateException());
}
assert(native_thread != NULL, "Starting null thread?");
if (native_thread->osthread() == NULL) {
delete native_thread;
if (JvmtiExport::should_post_resource_exhausted()) {
JvmtiExport::post_resource_exhausted(
JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
"unable to create new native thread");
}
THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
"unable to create new native thread");
}
Thread::start(native_thread);
JVM_END
这里除了做一些线程对象的初始化工作外,主要有两步操作:
1. native_thread = new JavaThread(&thread_entry, sz);创建一个JavaThread对象
2. 使用Thread::start(native_thread) 启动线程
而JavaThread的对象,实际位于hotspot的runtime的下thread.cpp文件,该文件主要定义了JVM中使用的Thread对象,根据其头文件描述,我们可以看到他主要有如下划分:
而JavaThread是hotspot的JVM实现的,对应于java用户定义的Thread对象的实际对象。jvm.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;
}
源码中我们可以看到在创建JavaThread的对象的时候,使用os::create_thread方法创建了一个系统线程,该线程对应于系统的内核线程,最终实际干活的为该线程。我们经常说java线程和实际执行的内核线程配比为1:1,应该就是对应于每个JavaThread的都创建了一个系统线程的与其对应。
JavaThread的对象创建完成,也创建了一个os的thread预期对应,接着调用了Thread::start(native_thread)的方法,启动线程,我们找到Thread的::start方法如下:
void Thread::start(Thread* thread) {
trace("start", thread);
if (!DisableStartThread) {
if (thread->is_Java_thread()) {
java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
java_lang_Thread::RUNNABLE);
}
os::start_thread(thread);
}
}
这里Thread的start方法直接调用os::start_thread启动了系统线程,到此java的线程就可以等待系统的调度执行了。
总结一下实际执行过程: