Thread start 源码揭秘
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. * * A zero status value corresponds to state "NEW". */ 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. */ 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 */ } } } 复制代码
从上面的代码中,我们可以发现它会先去 判断 threadStatus
是不是 0, 不是的话会抛出异常 。
在 Thread 源码中我们可以发现 threadStatus
默认值就是 0
- 那什么时候会被改变呢?
- 这个
threadStatus
都有哪些值呢?
嘿嘿 带着小小的疑问继续往下看叭~ 😝
start0
状态的改变肯定伴随着线程的启动,所以我们直接来到下面这个 start0
方法
private native void start0(); 复制代码
可以发现它是一个 native
函数,我们直接
OpenJDK 的 JDK
源码中全局搜索,就可以找到它了
path: jdk\src\share\native\java\lang\Thread.c
JNINativeMethod
这个 JNINativeMethod
是一个结构体
path: jdk\src\share\javavm\export\jni.h
/* * used in RegisterNatives to describe native method name, signature, * and function pointer. */ typedef struct { char *name; char *signature; void *fnPtr; } JNINativeMethod; 复制代码
JVM_StartThread
顺着思路,我们找到这个 JVM_StartThread
方法
path: jdk\src\share\javavm\export\jvm.h
/* * java.lang.Thread */ JNIEXPORT void JNICALL JVM_StartThread(JNIEnv *env, jobject thread); 复制代码
这里
JNIEXPORT
和JNICALL
都是JNI
的关键字,表示此函数是要被JNI
调用的
看到它是 JVM
开头的文件,我们来到下面这个 hotspot
的源码中找找看😋
发现这里有使用到👉
path: hotspot\src\share\vm\prims\jvm.cpp
代码:
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) JVMWrapper("JVM_StartThread"); JavaThread *native_thread = NULL; // We cannot hold the Threads_lock when we throw an exception, // due to rank ordering issues. Example: we might need to grab the // Heap_lock while we construct the exception. bool throw_illegal_thread_state = false; // We must release the Threads_lock before we can post a jvmti event // in Thread::start. { // Ensure that the C++ Thread and OSThread structures aren't freed before // we operate. MutexLocker mu(Threads_lock); // Since JDK 5 the java.lang.Thread threadStatus is used to prevent // re-starting an already started thread, so we should usually find // that the JavaThread is null. However for a JNI attached thread // there is a small window between the Thread object being created // (with its JavaThread set) and the update to its threadStatus, so we // have to check for this if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) { throw_illegal_thread_state = true; } else { // We could also check the stillborn flag to see if this thread was already stopped, but // for historical reasons we let the thread detect that itself when it starts running jlong size = java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread)); // Allocate the C++ Thread structure and create the native thread. The // stack size retrieved from java is signed, but the constructor takes // size_t (an unsigned type), so avoid passing negative values which would // result in really large stacks. size_t sz = size > 0 ? (size_t) size : 0; native_thread = new JavaThread(&thread_entry, sz); // At this point it may be possible that no osthread was created for the // JavaThread due to lack of memory. Check for this situation and throw // an exception if necessary. Eventually we may want to change this so // that we only grab the lock if the thread was created successfully - // then we can also do this check and throw the exception in the // JavaThread constructor. if (native_thread->osthread() != NULL) { // Note: the current thread is not being used within "prepare". 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) { // No one should hold a reference to the 'native_thread'. 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 复制代码
这里
JVM_ENTRY
和
JVM_END
是两个宏定义,定义了函数体的头和尾。 😄
这里太多细节了,啃不下呀 😅
讲几个大概的~ 咳咳 也就看看作者的注释翻译翻译🐷
比如:
创建线程
native_thread = new JavaThread(&thread_entry, sz); 复制代码
来到构造器中
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; } 复制代码
创建内核线程
发现这里是去 创建内核线程~ 👍
os::create_thread(this, thr_type, stack_sz); 复制代码
顺着思路继续探索~
针对 linux
的场景
有如下的代码 🐂
path: hotspot\src\os\linux\vm\os_linux.cpp
终于看到有点眼熟的函数了~ 哈哈哈
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread); 复制代码
底层内容真是让人抓狂~ 无止境的.. 哈哈哈
这里截下这个指针函数 java_start
,有兴趣的小伙伴继续往下冲冲冲!😄
启动线程
另一个重点 **Thread::start(native_thread); **
Thread::start
来到下面这里~
path: hotspot\src\share\vm\runtime\thread.cpp
第一个红框的作用:
在启动该线程之前,将线程状态初始化为 RUNNABLE。
不能在线程启动后设置,因为我们不知道 正确的线程状态,它可能在
MONITOR_WAIT 或 在睡眠或其他状态。
第二个红框的作用,启动内核线程!
// The INITIALIZED state is distinguished from the SUSPENDED state because the // conditions in which a thread is first started are different from those in which // a suspension is resumed. These differences make it hard for us to apply the // tougher checks when starting threads that we want to do when resuming them. // However, when start_thread is called as a result of Thread.start, on a Java // thread, the operation is synchronized on the Java Thread object. So there // cannot be a race to start the thread and hence for the thread to exit while // we are working on it. Non-Java threads that start Java threads either have // to do so in a context in which races are impossible, or should do appropriate // locking. 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); } 复制代码
嘿嘿 这里就直接搜搜看 pd_start_thread
啦,毕竟一看就知道是它去启动线程的 哈哈哈 😝
结果如图~
最后记录下这个 全局函数 notify
😝
bool Monitor::notify() { assert (_owner == Thread::current(), "invariant") ; assert (ILocked(), "invariant") ; if (_WaitSet == NULL) return true ; NotifyCount ++ ; // Transfer one thread from the WaitSet to the EntryList or cxq. // Currently we just unlink the head of the WaitSet and prepend to the cxq. // And of course we could just unlink it and unpark it, too, but // in that case it'd likely impale itself on the reentry. Thread::muxAcquire (_WaitLock, "notify:WaitLock") ; ParkEvent * nfy = _WaitSet ; if (nfy != NULL) { // DCL idiom _WaitSet = nfy->ListNext ; assert (nfy->Notified == 0, "invariant") ; // push nfy onto the cxq for (;;) { const intptr_t v = _LockWord.FullWord ; assert ((v & 0xFF) == _LBIT, "invariant") ; nfy->ListNext = (ParkEvent *)(v & ~_LBIT); if (CASPTR (&_LockWord, v, UNS(nfy)|_LBIT) == v) break; // interference - _LockWord changed -- just retry } // Note that setting Notified before pushing nfy onto the cxq is // also legal and safe, but the safety properties are much more // subtle, so for the sake of code stewardship ... OrderAccess::fence() ; nfy->Notified = 1; } Thread::muxRelease (_WaitLock) ; if (nfy != NULL && (NativeMonitorFlags & 16)) { // Experimental code ... light up the wakee in the hope that this thread (the owner) // will drop the lock just about the time the wakee comes ONPROC. nfy->unpark() ; } assert (ILocked(), "invariant") ; return true ; } 复制代码
惊喜,居然在这个代码中看到这段注释,这里提到 WaitSet , EntryList ,cxq
这是 隐式锁🔒 [[Synchronized 内部的实现原理呀]] 或者 称它为 [[Monitor机制]] 👍
WaitSet
是一个等待队列,存放进入等待状态的线程
cxq
是一个竞争队列,所有请求锁🔒的线程会先到这里
EntryList
存放cxq
中有资格成为候选资源去竞争锁的线程
// Transfer one thread from the WaitSet to the EntryList or cxq. // Currently we just unlink the head of the WaitSet and prepend to the cxq. // And of course we could just unlink it and unpark it, too, but // in that case it'd likely impale itself on the reentry. Thread::muxAcquire (_WaitLock, "notify:WaitLock") ; 复制代码
嘿嘿 后面深入锁的部分再分享😝
threadStatus
回到最上面,那还剩一个问题没解决~
这个 threadStatus
都有哪些值呢?
这里我们可以从 jvm.h 源码中获取到 👇
/* * Java thread state support */ enum { JAVA_THREAD_STATE_NEW = 0, JAVA_THREAD_STATE_RUNNABLE = 1, JAVA_THREAD_STATE_BLOCKED = 2, JAVA_THREAD_STATE_WAITING = 3, JAVA_THREAD_STATE_TIMED_WAITING = 4, JAVA_THREAD_STATE_TERMINATED = 5, JAVA_THREAD_STATE_COUNT = 6 };