Java 并发 - Thread 详解

Thread

Thread 类是我们比较常用的一个类,它用作于多线程的创建,内部实现的 Runnable 接口只是对用户传入 Runnable 方法的简单调用,Thread 类提供了很多可以控制线程的方法,比如 yield、sleep、start0、stop0,这些方法都是 native 方法,由具体平台的虚拟机的C++代码实现,只有了解了这些代码的作用才会对 Thread 类有一个新的了解,下面先从 Thread 构造方法来说:

    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

这里调用了 init 方法对 Thread 对象进行初始化

    // 调用 init 重载方法
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }

    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;

        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            
            if (security != null) {
                g = security.getThreadGroup();
            }
            
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }
        
        g.checkAccess();
        
        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);
                
        this.stackSize = stackSize;
        
        tid = nextThreadID();
    }

上面的代码没有什么特别的地方,无非就是将传入的参数赋值给对应的变量,而且这里似乎并没有创建线程,只是初始化了而已,那么真正创建的地方在哪呢,聪明的人应该会想到:Thread.start()

    public synchronized void start() {
        /**
         * threadStatus 为 0 表示线程状态为 NEW 新建
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
            
        group.add(this);

        boolean started = false;
        try {
            // 核心 native 方法
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
              
            }
        }
    }

    private native void start0();

start 方法里面几乎没什么逻辑,所有创建线程、启动线程的关键代码都在实现虚拟机的C++代码中,这里需要先下载一份 jdk_hotspot 的源码,然后找到源码,在介绍源码之前,我先补充一个知识点:JNI

JNI

JNI 全称 Java Native Interface,是 Java 平台的一部分,它提供了一些列的 API 允许 Java 和其他语言进行交互,实现了在 Java 代码中调用其他语言的函数。

那么在 Java 语言调用 native 方法的时候是通过一个命名规则来找对具体的方法的,这个命名规则就是 Java_ + 类全限定名(java_lang_thread)+ 方法名(start0),但是通常来讲这样的命名规则太复杂了,所以 JNI 又可以通过 registerNative() 方法来自定义方法命名的规则

    private static native void registerNatives();
    static {
        registerNatives();
    }

可以看到几乎每个包含有 native 方法的类里面都会有这么一个 native 方法的调用,这个方法这里就不具体展开了,它的主要功能就是将Java里面定义的方法名称和C语言里面定义的方法名称联系在一起,我们通过上述的命名规则拼接得到 Java_java_lang_Thread_registerNatives,然后根据该名称到 hotspot 源码中找到对应的代码

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]))

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

JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}

可以看到 methods 变量面包括了所有 Thread 里的 native 方法,RegisterNatives 方法则会将 methods 变量里的方法联系在一起,start0 在 hotspot 源代码里对应的是 JVM_StartThread 方法,接下来我们具体看一下 JVM_StartThread 方法:

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_StartThread");
  JavaThread *native_thread = NULL;

  bool throw_illegal_thread_state = false;

  {
    // 获取互斥锁
    MutexLocker mu(Threads_lock);

    // 这里检查了一遍 jthread 是否为空,是为了防止线程被重复启动
    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));
      
      size_t sz = size > 0 ? (size_t) size : 0;
      // 操作系统层面创建线程
      native_thread = new JavaThread(&thread_entry, sz);

      if (native_thread->osthread() != NULL) {
        // 将 native_thread 与 java_lang_thread 关联起来
        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) {
    // osthread 创建失败则删除 JavaThread 并抛出异常
    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

接着往下看 new JavaThread 方法

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);
  }
  // 初始化 thread 的各个属性
  initialize();
  _jni_attach_state = _not_attaching_via_jni;
  set_entry_point(entry_point);
  
  os::ThreadType thr_type = os::java_thread;
  thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :
                                                     os::java_thread;
  // 创建 os 线程
  os::create_thread(this, thr_type, stack_sz);
}

不同的操作系统有着不同的 os::create_thread 实现,这里选取 linux 里的代码片段

bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
  assert(thread->osthread() == NULL, "caller responsible");

  // 分配 OSThread 对象
  OSThread* osthread = new OSThread(NULL, NULL);
  if (osthread == NULL) {
    return false;
  }
  
  osthread->set_thread_type(thr_type);

  // 默认初始化状态为 ALLOCATED
  osthread->set_state(ALLOCATED);

  thread->set_osthread(osthread);

  // 初始化线程属性
  pthread_attr_t attr;
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  
  if (os::Linux::supports_variable_stack_size()) {
    
    if (stack_size == 0) {
      stack_size = os::Linux::default_stack_size(thr_type);

      switch (thr_type) {
      case os::java_thread:
        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;
        }
      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;
      }
    }

    stack_size = MAX2(stack_size, os::Linux::min_stack_allowed);
    pthread_attr_setstacksize(&attr, stack_size);
  } else {
    
  }

  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;
    // 重点pthread_create是UNIX环境创建线程的函数,里面的参数分别为:
    // 指向线程标识符的指针 线程属性 线程要运行的函数开始地址 运行函数所需要的参数
    int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);

    pthread_attr_destroy(&attr);

    // 如果成功会返回0
    if (ret != 0) {
      if (PrintMiscellaneous && (Verbose || WizardMode)) {
        perror("pthread_create()");
      }
      
      thread->set_osthread(NULL);
      delete osthread;
      if (lock) os::Linux::createThread_lock()->unlock();
      return false;
    }

    // 存储 pthread 的信息到 osthread
    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();
    }
  }

  // 中止状态下清除 osthread
  if (state == ZOMBIE) {
      thread->set_osthread(NULL);
      delete osthread;
      return false;
  }
  
  assert(state == INITIALIZED, "race condition");
  return true;
}

可以看到在创建线程的过程中,底层实际上调用的是 pthread_create 函数,同时会进行一些列属性的初始化,接着我们回到 JVM_StartThread 方法去,看下线程启动的时候又做了什么 Thread::start(native_thread)

void Thread::start(Thread* thread) {
  trace("start", thread);
  
  if (!DisableStartThread) {
    if (thread->is_Java_thread()) {
      // 修改 JavaThread 的状态为 RUNNABLE
      java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
                                          java_lang_Thread::RUNNABLE);
    }
    // 启动os线程
    os::start_thread(thread);
  }
}

void os::start_thread(Thread* thread) {
  // 获取互斥锁
  MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);
  OSThread* osthread = thread->osthread();
  // 设置 osthread 状态为 RUNNABLE
  osthread->set_state(RUNNABLE);
  // 启动线程
  pd_start_thread(thread);
}

void os::pd_start_thread(Thread* thread) {
  OSThread * osthread = thread->osthread();
  assert(osthread->get_state() != INITIALIZED, "just checking");
  Monitor* sync_with_child = osthread->startThread_lock();
  MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag);
  // 唤起当前线程
  sync_with_child->notify();
}

可以看到 Thread::start(native_thread) 方法主要是修改了 JavaThread、OSThread 的状态为 RUNNABLE,然后在唤起线程去执行任务

总结

在 Java 中,刚开始 new Thread 线程的时候是不会创建对应的操作系统线程的,而是在 Thread.start() 方法开始的时候去创建,在 c++ 代码中,会创建 JavaThread 对象、OSThread 对象,并将真正创建好的线程地址绑定在这两个对象中,同时阻塞子线程,等到后续调用 Thread::start 方法的时候,会将状态改为 RUNNABLE 同时唤起子线程去执行任务

你可能感兴趣的:(源码解读,java,c++)