說到Thread你們都很熟悉,咱們日常寫併發代碼的時候都會接觸到,那麼咱們來看看下面這段代碼是如何初始化以及執行的呢?html
public class ThreadDemo { public static void main(String[] args) { new Thread().start(); } }
代碼就一行很簡單,那麼這行簡單的代碼背後作了那些事情呢?java
首先JVM會去加載Thread的字節碼,初始化這個類,這裏即調用下面這段代碼:linux
public class Thread implements Runnable { /* Make sure registerNatives is the first thing <clinit> does. */ private static native void registerNatives(); static { registerNatives(); } }
是個native方法,那麼咱們去看看內部實現是什麼,具體的目錄是openjdk/jdk/src/share/native/java/lang/Thread.c
, 下載地址windows
#define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0])) //JVM前綴開頭的方法,具體實如今JVM中 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 //jclass cls即爲java.lang.Thread JNIEXPORT void JNICALL Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls) { (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods)); }
能夠發現具體的實現都是由這些JVM開頭的方法決定的,而這幾個方法的具體實現都在hotspot\src\share\vm\prims\jvm.cpp
文件中,而RegisterNatives我目前的理解其實相似一個方法表,從Java方法到native方法的一個映射,具體的原理後面再研究。緩存
其實就是一些賦值,名字、線程ID這些,這兩個變量都是static,用synchronized修飾,保證線程安全性。安全
public Thread() { //nextThreadNum就是變量的自增,用synchronized修飾保證可見性 init(null, null, "Thread-" + nextThreadNum(), 0); } private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null); } private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) { if (name == null) { throw new NullPointerException("name cannot be null"); } this.name = name; Thread parent = currentThread(); // 安全相關的一坨東西.... /* Stash the specified stack size in case the VM cares */ this.stackSize = stackSize; /* Set thread ID */ tid = nextThreadID(); } private static synchronized long nextThreadID() { return ++threadSeqNumber; }
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) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } }
這裏start0()是個native方法,對應jvm.cpp中的JVM_StartThread,咱們看到不少方法都是JVM_ENTRY開頭,JVM_END結尾,相似於{}的做用,這裏是將不少公共的操做封裝到了JVM_ENTRY裏面.併發
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. { // 加鎖 MutexLocker mu(Threads_lock); // 自從JDK 5以後 java.lang.Thread#threadStatus能夠用來阻止重啓一個已經啓動 // 的線程,因此這裏的JavaThread一般爲空。然而對於一個和JNI關聯的線程來講,在線程 // 被建立和更新他的threadStatus以前會有一個小窗口,所以必須檢查這種狀況 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)); // 爲C++線程結構體分配內存並建立native線程。從java取出的stack size是有符號的,所以這裏 // 須要進行一次轉換,避免傳入負數致使建立一個很是大的棧。 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
基本上這裏就是先加鎖,作些檢查,而後建立JavaThread,若是建立成功的話會調用prepare(),而後是一些異常處理,沒有異常的話最後會啓動線程,那麼下面咱們先來看看JavaThread是如何被建立的。app
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();//這個方法其實就是一堆變量的初始化,不是Null就是0. _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; // 根據傳進來的entry_point判斷要建立的線程的類型。 thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread : os::java_thread; os::create_thread(this, thr_type, stack_sz); // _osthread多是Null,所以咱們耗盡了內存(太多的活躍線程)。咱們須要拋出OOM,然而不能在這作,由於調用者可能 // 還持有鎖,而全部的鎖都必須在拋出異常以前被釋放。 // 代碼執行到這,線程仍是suspended狀態,由於線程必須被建立者直接啓動。 } void JavaThread::initialize() { // Initialize fields // ... set_thread_state(_thread_new); // 線程的初始狀態 // ... }
JavaThreadState記錄了線程記錄了線程正在執行的代碼在哪一部分,這個信息可能會被安全點使用到(GC),最核心的有四種:less
_thread_new
剛開始啓動,但還沒執行初始化代碼,更可能還在OS初始化的層面jvm
_thread_in_native
在native代碼中
_thread_in_vm
在vm中執行
_thread_in_Java
執行在解釋或者編譯後的Java代碼中
每一個狀態都會對應一箇中間的轉換狀態,這些額外的中間狀態使得安全點的代碼可以更快的處理某一線程狀態而不用掛起線程。
enum JavaThreadState { _thread_uninitialized = 0, // should never happen (missing initialization) _thread_new = 2, // just starting up, i.e., in process of being initialized _thread_new_trans = 3, // corresponding transition state (not used, included for completness) _thread_in_native = 4, // running in native code _thread_in_native_trans = 5, // corresponding transition state _thread_in_vm = 6, // running in VM _thread_in_vm_trans = 7, // corresponding transition state _thread_in_Java = 8, // running in Java or in stub code _thread_in_Java_trans = 9, // corresponding transition state (not used, included for completness) _thread_blocked = 10, // blocked in vm _thread_blocked_trans = 11, // corresponding transition state _thread_max_state = 12 // maximum thread state+1 - used for statistics allocation };
咱們看到 os::create_thread(this, thr_type, stack_sz);
這行代碼會去實際的建立線程,首先咱們知道Java宣傳的是一次編譯,處處運行,那麼到底是怎麼作到在不一樣的CPU、操做系統上還可以保持良好的可移植性呢?
// 平臺相關的東東 #ifdef TARGET_OS_FAMILY_linux # include "os_linux.hpp" # include "os_posix.hpp" #endif #ifdef TARGET_OS_FAMILY_solaris # include "os_solaris.hpp" # include "os_posix.hpp" #endif #ifdef TARGET_OS_FAMILY_windows # include "os_windows.hpp" #endif #ifdef TARGET_OS_FAMILY_aix # include "os_aix.hpp" # include "os_posix.hpp" #endif #ifdef TARGET_OS_FAMILY_bsd # include "os_posix.hpp" # include "os_bsd.hpp" #endif #ifdef TARGET_OS_ARCH_linux_x86 # include "os_linux_x86.hpp" #endif #ifdef TARGET_OS_ARCH_linux_sparc # include "os_linux_sparc.hpp" #endif #ifdef TARGET_OS_ARCH_linux_zero # include "os_linux_zero.hpp" #endif #ifdef TARGET_OS_ARCH_solaris_x86 # include "os_solaris_x86.hpp" #endif #ifdef TARGET_OS_ARCH_solaris_sparc # include "os_solaris_sparc.hpp" #endif #ifdef TARGET_OS_ARCH_windows_x86 # include "os_windows_x86.hpp" #endif #ifdef TARGET_OS_ARCH_linux_arm # include "os_linux_arm.hpp" #endif #ifdef TARGET_OS_ARCH_linux_ppc # include "os_linux_ppc.hpp" #endif #ifdef TARGET_OS_ARCH_aix_ppc # include "os_aix_ppc.hpp" #endif #ifdef TARGET_OS_ARCH_bsd_x86 # include "os_bsd_x86.hpp" #endif #ifdef TARGET_OS_ARCH_bsd_zero # include "os_bsd_zero.hpp" #endif
咱們看到os.hpp中有這樣一段代碼,可以根據不一樣的操做系統選擇include不一樣的頭文件,從而將平臺相關的邏輯封裝到對應的庫文件中,咱們這裏以linux爲例,create_thread最終會調用os_linux.cpp中的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); if (osthread == NULL) { return false; } // set the correct thread state osthread->set_thread_type(thr_type); // 初始狀態爲ALLOCATED,而不是INITIALIZED osthread->set_state(ALLOCATED); thread->set_osthread(osthread); // init thread attributes pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // stack size if (os::Linux::supports_variable_stack_size()) { // 若是上層未傳遞則計算stack_size if (stack_size == 0) { //若是爲compiler_thread,則分配4M,不然默認會分配1M stack_size = os::Linux::default_stack_size(thr_type); switch (thr_type) { case os::java_thread: // Java線程用ThreadStackSize,這個值能夠經過-Xss指定 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; } // else fall through: // use VMThreadStackSize if CompilerThreadStackSize is not defined 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; } } // 用二者較大的那個,min_stack_allowed默認爲128K stack_size = MAX2(stack_size, os::Linux::min_stack_allowed); pthread_attr_setstacksize(&attr, stack_size); } else { // let pthread_create() pick the default value. } // glibc guard page 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; // Linux用於建立線程的函數,這個線程經過執行java_start來啓動,其中thread是做爲java_start的參數傳遞進來的 // 具體可見手冊:http://man7.org/linux/man-pages/man3/pthread_create.3.html int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread); pthread_attr_destroy(&attr); if (ret != 0) { // 建立失敗,將_osthread置爲空,還記得在jvm.cpp的JVM_StartThread中會根據_osthread是否爲空來判斷 // 是否建立成功 if (PrintMiscellaneous && (Verbose || WizardMode)) { perror("pthread_create()"); } // 清理資源,並解鎖 thread->set_osthread(NULL); delete osthread; if (lock) os::Linux::createThread_lock()->unlock(); return false; } // 建立成功會將底層線程的ID保存在tid中 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(); } } // 線程的數目達到極限了 if (state == ZOMBIE) { thread->set_osthread(NULL); delete osthread; return false; } // The thread is returned suspended (in state INITIALIZED), // and is started higher up in the call chain assert(state == INITIALIZED, "race condition"); return true; }
下面咱們來看看pthread_create會執行的回調函數java_start,這個方法是全部新建立的線程必走的流程。
static void *java_start(Thread *thread) { // 嘗試隨機化熱棧幀高速緩存行的索引,這有助於優化擁有相同棧幀線程去互相驅逐彼此的緩存行時,線程 // 能夠是同一個JVM實例或者不一樣的JVM實例,這尤爲有助於擁有超線程技術的處理器。 static int counter = 0; int pid = os::current_process_id(); alloca(((pid ^ counter++) & 7) * 128); ThreadLocalStorage::set_thread(thread); OSThread* osthread = thread->osthread(); Monitor* sync = osthread->startThread_lock(); // non floating stack LinuxThreads needs extra check, see above if (!_thread_safety_check(thread)) { // notify parent thread MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag); osthread->set_state(ZOMBIE); sync->notify_all(); return NULL; } // thread_id is kernel thread id (similar to Solaris LWP id) osthread->set_thread_id(os::Linux::gettid()); if (UseNUMA) { int lgrp_id = os::numa_get_group_id(); if (lgrp_id != -1) { thread->set_lgrp_id(lgrp_id); } } // initialize signal mask for this thread os::Linux::hotspot_sigmask(thread); // initialize floating point control register os::Linux::init_thread_fpu_state(); // handshaking with parent thread { MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag); // 設置爲已經初始化完成,並notify父線程 osthread->set_state(INITIALIZED); sync->notify_all(); // wait until os::start_thread() while (osthread->get_state() == INITIALIZED) { sync->wait(Mutex::_no_safepoint_check_flag); } } //這裏上層傳遞過來的是JavaThread,所以會調用JavaThread#run()方法 thread->run(); return 0; } void JavaThread::run() { // 初始化本地線程分配緩存(TLAB)相關的屬性 this->initialize_tlab(); // used to test validitity of stack trace backs this->record_base_of_stack_pointer(); // Record real stack base and size. this->record_stack_base_and_size(); // Initialize thread local storage; set before calling MutexLocker this->initialize_thread_local_storage(); this->create_stack_guard_pages(); this->cache_global_variables(); // 將線程的狀態更改成_thread_in_vm,線程已經能夠被VM中的安全點相關的代碼處理了,也就是說必須 // JVM若是線程在執行native裏面的代碼,是搞不了安全點的,待確認 ThreadStateTransition::transition_and_fence(this, _thread_new, _thread_in_vm); assert(JavaThread::current() == this, "sanity check"); assert(!Thread::current()->owns_locks(), "sanity check"); DTRACE_THREAD_PROBE(start, this); // This operation might block. We call that after all safepoint checks for a new thread has // been completed. this->set_active_handles(JNIHandleBlock::allocate_block()); if (JvmtiExport::should_post_thread_life()) { JvmtiExport::post_thread_start(this); } EventThreadStart event; if (event.should_commit()) { event.set_javalangthread(java_lang_Thread::thread_id(this->threadObj())); event.commit(); } // 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(); // Note, thread is no longer valid at this point! } 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); // 這個entry_point就是JVM_StartThread中傳遞過來的那個,也就是thread_entry this->entry_point()(this, this); } DTRACE_THREAD_PROBE(stop, this); this->exit(false); delete this; }
咱們最後再看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); }
vmSymbols,這個是JVM對於那些須要特殊處理的類、方法等的聲明,個人理解就是一個方法表,根據下面這行代碼能夠看出來,其實調用的就是run()方法.
/* common method and field names */ template(run_method_name, "run")
而後咱們回到JVM_StartThread方法中,這裏會接着調用prepare()方法,設置線程優先級(將Java中的優先級映射到os中),而後添加到線程隊列中去.最後會調用Thread::start(native_thread);
啓動線程。
void Thread::start(Thread* thread) { trace("start", thread); // start和resume不同,start被synchronized修飾 if (!DisableStartThread) { if (thread->is_Java_thread()) { // 在啓動線程以前初始化線程的狀態爲RUNNABLE,爲啥不能在以後設置呢?由於啓動以後多是 // 等待或者睡眠等其餘狀態,具體是什麼咱們不知道 java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(), java_lang_Thread::RUNNABLE); } os::start_thread(thread); } }
一個Java線程對應一個JavaThread->OSThread -> Native Thread
在調用java.lang.Thread#start()方法以前不會啓動線程,僅僅調用run()方法只是會在當前線程運行而已
//todo