Thread 类是我们比较常用的一个类,它用作于多线程的创建,内部实现的 Runnable 接口只是对用户传入 Runnable 方法的简单调用,Thread 类提供了很多可以控制线程的方法,比如 yield、sleep、start0、stop0,这些方法都是 native 方法,由具体平台的虚拟机的C++代码实现,只有了解了这些代码的作用才会对 Thread 类有一个新的了解,下面先从 Thread 构造方法来说:
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
这里调用了 init 方法对 Thread 对象进行初始化
// 调用 init 重载方法
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
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();
}
上面的代码没有什么特别的地方,无非就是将传入的参数赋值给对应的变量,而且这里似乎并没有创建线程,只是初始化了而已,那么真正创建的地方在哪呢,聪明的人应该会想到:Thread.start()
public synchronized void start() {
/**
* threadStatus 为 0 表示线程状态为 NEW 新建
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
// 核心 native 方法
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
private native void start0();
start 方法里面几乎没什么逻辑,所有创建线程、启动线程的关键代码都在实现虚拟机的C++代码中,这里需要先下载一份 jdk_hotspot 的源码,然后找到源码,在介绍源码之前,我先补充一个知识点:JNI
JNI 全称 Java Native Interface,是 Java 平台的一部分,它提供了一些列的 API 允许 Java 和其他语言进行交互,实现了在 Java 代码中调用其他语言的函数。
那么在 Java 语言调用 native 方法的时候是通过一个命名规则来找对具体的方法的,这个命名规则就是 Java_ + 类全限定名(java_lang_thread)+ 方法名(start0),但是通常来讲这样的命名规则太复杂了,所以 JNI 又可以通过 registerNative() 方法来自定义方法命名的规则
private static native void registerNatives();
static {
registerNatives();
}
可以看到几乎每个包含有 native 方法的类里面都会有这么一个 native 方法的调用,这个方法这里就不具体展开了,它的主要功能就是将Java里面定义的方法名称和C语言里面定义的方法名称联系在一起,我们通过上述的命名规则拼接得到 Java_java_lang_Thread_registerNatives,然后根据该名称到 hotspot 源码中找到对应的代码
#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));
}
可以看到 methods 变量面包括了所有 Thread 里的 native 方法,RegisterNatives 方法则会将 methods 变量里的方法联系在一起,start0 在 hotspot 源代码里对应的是 JVM_StartThread 方法,接下来我们具体看一下 JVM_StartThread 方法:
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);
// 这里检查了一遍 jthread 是否为空,是为了防止线程被重复启动
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) {
// 将 native_thread 与 java_lang_thread 关联起来
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) {
// osthread 创建失败则删除 JavaThread 并抛出异常
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
接着往下看 new 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);
}
// 初始化 thread 的各个属性
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 线程
os::create_thread(this, thr_type, stack_sz);
}
不同的操作系统有着不同的 os::create_thread 实现,这里选取 linux 里的代码片段
bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
assert(thread->osthread() == NULL, "caller responsible");
// 分配 OSThread 对象
OSThread* osthread = new OSThread(NULL, NULL);
if (osthread == NULL) {
return false;
}
osthread->set_thread_type(thr_type);
// 默认初始化状态为 ALLOCATED
osthread->set_state(ALLOCATED);
thread->set_osthread(osthread);
// 初始化线程属性
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (os::Linux::supports_variable_stack_size()) {
if (stack_size == 0) {
stack_size = os::Linux::default_stack_size(thr_type);
switch (thr_type) {
case os::java_thread:
assert (JavaThread::stack_size_at_create() > 0, "this should be set");
stack_size = JavaThread::stack_size_at_create();
break;
case os::compiler_thread:
if (CompilerThreadStackSize > 0) {
stack_size = (size_t)(CompilerThreadStackSize * K);
break;
}
case os::vm_thread:
case os::pgc_thread:
case os::cgc_thread:
case os::watcher_thread:
if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K);
break;
}
}
stack_size = MAX2(stack_size, os::Linux::min_stack_allowed);
pthread_attr_setstacksize(&attr, stack_size);
} else {
}
pthread_attr_setguardsize(&attr, os::Linux::default_guard_size(thr_type));
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;
// 重点pthread_create是UNIX环境创建线程的函数,里面的参数分别为:
// 指向线程标识符的指针 线程属性 线程要运行的函数开始地址 运行函数所需要的参数
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
pthread_attr_destroy(&attr);
// 如果成功会返回0
if (ret != 0) {
if (PrintMiscellaneous && (Verbose || WizardMode)) {
perror("pthread_create()");
}
thread->set_osthread(NULL);
delete osthread;
if (lock) os::Linux::createThread_lock()->unlock();
return false;
}
// 存储 pthread 的信息到 osthread
osthread->set_pthread_id(tid);
// 等待子线程初始化或中止
{
Monitor* sync_with_child = osthread->startThread_lock();
MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag);
while ((state = osthread->get_state()) == ALLOCATED) {
sync_with_child->wait(Mutex::_no_safepoint_check_flag);
}
}
if (lock) {
os::Linux::createThread_lock()->unlock();
}
}
// 中止状态下清除 osthread
if (state == ZOMBIE) {
thread->set_osthread(NULL);
delete osthread;
return false;
}
assert(state == INITIALIZED, "race condition");
return true;
}
可以看到在创建线程的过程中,底层实际上调用的是 pthread_create 函数,同时会进行一些列属性的初始化,接着我们回到 JVM_StartThread 方法去,看下线程启动的时候又做了什么 Thread::start(native_thread)
void Thread::start(Thread* thread) {
trace("start", thread);
if (!DisableStartThread) {
if (thread->is_Java_thread()) {
// 修改 JavaThread 的状态为 RUNNABLE
java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
java_lang_Thread::RUNNABLE);
}
// 启动os线程
os::start_thread(thread);
}
}
void os::start_thread(Thread* thread) {
// 获取互斥锁
MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);
OSThread* osthread = thread->osthread();
// 设置 osthread 状态为 RUNNABLE
osthread->set_state(RUNNABLE);
// 启动线程
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();
}
可以看到 Thread::start(native_thread) 方法主要是修改了 JavaThread、OSThread 的状态为 RUNNABLE,然后在唤起线程去执行任务
在 Java 中,刚开始 new Thread 线程的时候是不会创建对应的操作系统线程的,而是在 Thread.start() 方法开始的时候去创建,在 c++ 代码中,会创建 JavaThread 对象、OSThread 对象,并将真正创建好的线程地址绑定在这两个对象中,同时阻塞子线程,等到后续调用 Thread::start 方法的时候,会将状态改为 RUNNABLE 同时唤起子线程去执行任务