3.2 Thread面試必問 jdk原始碼解析
技術標籤:Java# 3 多執行緒和併發javaThread面試必問Thread原始碼解析多執行緒jvm中thread原始碼解析
3.2 初識執行緒
執行緒(thread)是作業系統能夠進行運算排程的最小單位。當執行java main方法的時候,jvm就會建立一個執行緒來執行我們編寫的java程式,當執行多執行緒程式的時候,jvm會建立新的執行緒來執行該執行緒執行的程式碼段。
3.2.1 Java建立和啟動執行緒
建立執行緒有兩種方式,第一種直接new Thread(),重寫run()方法,呼叫該執行緒的start()方法即可。第二種是繼承Thread類,重寫run()方法,呼叫start()方法啟動執行緒。
public static void main(String args[]){
new Thread(()->{
System.out.print(Thread.currentThread());
System.out.println("hello world");
}).start();
}
3.2.2 建立Thread
Thread類的空參建構函式,會呼叫一個初始化方法init方法。該方法時初始化當前示例的一些屬性的。
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
Thread類帶引數構造,第一個引數是執行緒組,第一個Runnable實列,第三個是名字。
public Thread(ThreadGroup group, Runnable target, String name) {
init(group, target, name, 0);
}
init()方法:
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
init方法呼叫了一個過載的另一個init方法,原始碼如下:
/**
* Initializes a Thread.
*
* @param g the Thread group
* @param target the object whose run() method gets called
* @param name the name of the new Thread
* @param stackSize the desired stack size for the new thread, or
* zero to indicate that this parameter is to be ignored.
* @param acc the AccessControlContext to inherit, or
* AccessController.getContext() if null
* @param inheritThreadLocals if {@code true}, inherit initial values for
* inheritable thread-locals from the constructing thread
*/
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;
//建立該執行緒的執行緒,如果在主執行緒main中建立parent就是main主執行緒。
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
//附加到父類執行緒的執行緒組
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
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);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
3.2.4 執行緒啟動
呼叫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 {
//真正的啟動方法,是一個native方法
start0();
//native方法執行了,啟動才算成功,然後設定為true
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 */
}
}
}
真正的啟動方法,是一個native方法
private native void start0();
3.2.3 Java Thread啟動方法一探究竟
在Thread類裡,頭部有這樣一段程式碼,這也就意味著在啟動一個執行緒時,會先執行registerNatives()方法,該方法是一個native方法,native方法表示該方法並不是Java的實現,而是底層C或者C++的實現,那麼這個方法究竟是幹嘛的呢?
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
}
找到Jdk原始碼,可以看到有一個methods[],裡邊的名字是不是很熟悉,這個檔案是在jdk-master目錄裡的src/java.base/share/native/libjava/Thread.c,大家可以點進去看下。
#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]))
//這串方法名是不是有些熟悉,不是在Thread裡出現過嗎
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},
{"interrupt0", "()V", (void *)&JVM_Interrupt},
{"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));
}
JNIEXPORT void JNICALL
Java_java_lang_Thread_clearInterruptEvent(JNIEnv *env, jclass cls)
{
#if defined(_WIN32)
// Need to reset the interrupt event used by Process.waitFor
ResetEvent((HANDLE) JVM_GetThreadInterruptEvent());
#endif
}
通過methods陣列可以發現其實裡邊的方法是給執行緒設定屬性或者管理執行緒宣告週期的。先來看下JVM_StartThread方法,首先啟動一個執行緒的時候會新建一個執行緒。
{
// Ensure that the C++ Thread and OSThread structures aren't freed before we operate.
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));
NOT_LP64(if (size > SIZE_MAX) size = SIZE_MAX;)
size_t sz = size > 0 ? (size_t) size : 0;
//真正建立執行緒
native_thread = new JavaThread(&thread_entry, sz);
if (native_thread->osthread() != NULL) {
//此處標示執行緒已經建立好了
native_thread->prepare(jthread);
}
}
}
JNIHandles::resolve_non_null(jthread)) != NULL
上邊這行程式碼是檢查jvm管理的執行緒,在Java Thread類中有一個threadStatus的狀態,會標示執行緒是否啟動狀態。
/* Java thread status for tools,
* initialized to indicate thread 'not yet started'
*/
private volatile int threadStatus = 0;
jdk原始碼c++啟動一個執行緒過程中就會去檢查該狀態,java建立一個執行緒託管給jvm去建立有一個空窗期,上邊C++程式碼做的就是防止空窗期一個執行緒重複建立。然後下邊這行程式碼就是真正new 一個Java執行緒。
native_thread = new JavaThread(&thread_entry, sz);
建立好了之後,就是真正要執行的方法了,今天就寫到這兒,其中因為原始碼很難除錯,就不再深入,感興趣的可研究,歡迎與筆者交流。
Thread::start(native_thread);
最後提示:作業系統建立執行緒的方法。
os_linux.cpp # os::create_thread()