转载

Java如何获取当前线程

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并发篇

跟我交流,向我提问:

Java如何获取当前线程

公众号的菜单已分为“读书总结”、“分布式”、“机器学习”、“深度学习”、“NLP”、“Java深度”、“Java并发核心”、“JDK源码”、“Tomcat内核”等,可能有一款适合你的胃口。

为什么写《Tomcat内核设计剖析》

欢迎关注:

Java如何获取当前线程
原文  https://juejin.im/post/5b3d65e0e51d45191c7e1895
正文到此结束
Loading...