Java如何獲取當前執行緒

超人汪小建發表於2018-07-05

前言

Java 中經常會遇到要獲取當前執行緒的情況,這時一般我們就會通過Thread.currentThread()來獲取,接下去就看看執行該語句在 JVM 中做了什麼吧。

簡單例子

以下是一個簡單的例子,獲取當前執行緒並列印執行緒名稱,輸出是"main",即主執行緒。

public class CurrentThreadTest {

	public static void main(String[] args) {
		Thread t = Thread.currentThread();
		System.out.println(t.getName());
	}

}
複製程式碼

currentThread方法

在 Thread 類中,currentThread是一個靜態且本地方法。

public static native Thread currentThread();
複製程式碼

Thread.c

Java 層宣告的本地方法對應實現在 Thread.c 中,currentThread是一個註冊到 JVM 中的方法,它與 JVM 中的JVM_CurrentThread函式繫結了,所以實現邏輯在JVM_CurrentThread函式裡。邏輯為:

  • JVMWrapper("JVM_CurrentThread")用於除錯。
  • 通過thread->threadObj()獲取 oop,這裡的 thread 是在JNI_ENTRY巨集中獲取到的,詳細情況可參考後面的JNI_ENTRYJNI_END巨集。
  • 呼叫JNIHandles::make_local函式
#define THD "Ljava/lang/Thread;"

static JNINativeMethod methods[] = {
    ...
    {"currentThread",    "()" THD,     (void *)&JVM_CurrentThread},
    ...
};

JVM_ENTRY(jobject, JVM_CurrentThread(JNIEnv* env, jclass threadClass))
  JVMWrapper("JVM_CurrentThread");
  oop jthread = thread->threadObj();
  assert (thread != NULL, "no current thread!");
  return JNIHandles::make_local(env, jthread);
JVM_END
複製程式碼

make_local函式中主要看thread_from_jni_environment函式,它用於獲取當前執行緒,它的邏輯為JavaThread *thread_from_jni_env = (JavaThread*)((intptr_t)env - in_bytes(jni_environment_offset()));,即直接通過地址偏移來做減法計算得到JavaThread*,這是因為 JavaThread 物件包含了 JNIEnv 物件屬性,所以可以通過JNIEnv*與偏移做減法來算出JavaThread*。最後還要檢查執行緒是否已經終止狀態,沒有終止才返回該執行緒物件。

獲取到JavaThread*物件後,分配控制程式碼並將 oop 賦給控制程式碼,並且轉成 Java 層的物件 jobject。

jobject JNIHandles::make_local(JNIEnv* env, oop obj) {
  if (obj == NULL) {
    return NULL;                
  } else {
    JavaThread* thread = JavaThread::thread_from_jni_environment(env);
    assert(Universe::heap()->is_in_reserved(obj), "sanity check");
    return thread->active_handles()->allocate_handle(obj);
  }
}

static JavaThread* thread_from_jni_environment(JNIEnv* env) {
    JavaThread *thread_from_jni_env = (JavaThread*)((intptr_t)env - in_bytes(jni_environment_offset()));
    if (thread_from_jni_env->is_terminated()) {
      thread_from_jni_env->block_if_vm_exited();
      return NULL;
    } else {
      return thread_from_jni_env;
    }
  }
複製程式碼

JNI_ENTRYJNI_END巨集

這兩個巨集將共同的部分都抽離出來了。其中JNI_END比較簡單,就兩個結束大括號。

#define JNI_ENTRY(result_type, header)  JNI_ENTRY_NO_PRESERVE(result_type, header)    WeakPreserveExceptionMark __wem(thread);

#define JNI_END } }
複製程式碼

JNI_ENTRY主要邏輯:

  • 獲取當前執行執行緒 JavaThread 指標物件。
  • 建立 ThreadInVMfromNative 物件。
  • TRACE_CALL ,這裡什麼都不幹。
  • 建立 HandleMarkCleaner 物件。
  • 將 thread 賦值給 Exceptions 中的 THREAD。
  • 校驗棧對齊。
  • 建立 WeakPreserveExceptionMark 物件。
#define JNI_ENTRY_NO_PRESERVE(result_type, header)                   \
extern "C" {                                                         \
  result_type JNICALL header {                                       \
    JavaThread* thread=JavaThread::thread_from_jni_environment(env); \
    assert( !VerifyJNIEnvThread || (thread == Thread::current()), "JNIEnv is only valid in same thread"); \
    ThreadInVMfromNative __tiv(thread);                              \
    debug_only(VMNativeEntryWrapper __vew;)                          \
    VM_ENTRY_BASE(result_type, header, thread)
    
#define VM_ENTRY_BASE(result_type, header, thread)                   \
  TRACE_CALL(result_type, header)                                    \
  HandleMarkCleaner __hm(thread);                                    \
  Thread* THREAD = thread;                                           \
  os::verify_stack_alignment();      
複製程式碼

-------------推薦閱讀------------

我的2017文章彙總——機器學習篇

我的2017文章彙總——Java及中介軟體

我的2017文章彙總——深度學習篇

我的2017文章彙總——JDK原始碼篇

我的2017文章彙總——自然語言處理篇

我的2017文章彙總——Java併發篇


跟我交流,向我提問:

這裡寫圖片描述

公眾號的選單已分為“讀書總結”、“分散式”、“機器學習”、“深度學習”、“NLP”、“Java深度”、“Java併發核心”、“JDK原始碼”、“Tomcat核心”等,可能有一款適合你的胃口。

為什麼寫《Tomcat核心設計剖析》

歡迎關注:

這裡寫圖片描述

相關文章