无论用哪种方式实现多线程,最终究其根源都是在调用Thread类的start()方法。而此时就有一个疑问了,若要实现多线程需要覆写其run()方法,而为什么要使用start()启动线程而不直接调用run()方法?

  • 1.首先来看一下start()方法的源码,看看这个方法究竟在干什么事情。
    /**
     * Causes this thread to begin execution; the Java Virtual Machine
     * calls the <code>run</code> method of this thread.
     * <p>
     * The result is that two threads are running concurrently: the
     * current thread (which returns from the call to the
     * <code>start</code> method) and the other thread (which executes its
     * <code>run</code> method).
     * <p>
     * It is never legal to start a thread more than once.
     * In particular, a thread may not be restarted once it has completed
     * execution.
     *
     * @exception  IllegalThreadStateException  if the thread was already
     *               started.
     * @see        #run()
     * @see        #stop()
     */
    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 {
            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 */
            }
        }
    }

从整个方法上的注释看,start()使得该线程开始执行,Java虚拟机调用这个线程的run()方法,结果是两个线程会并发的运行:当前线程(调用start()方法的"主线程")与另一个线程(执行其run方法)

  • 2.3.start()之后的代码的核心就是其中调用的start0()方法,下面看看start0()的定义:
    private native void start0();

start0 是一个本地的方法,start0()这个方法是在Thread的静态块中来注册的,其作用就是注册一些本地方法提供给Thread类来使用,比如start0()、isAlive()… ,具体方法在一个C的文件中Thread.c,其定义了各个操作系统平台要用的关于线程的公共数据和操作,具体定义了很多的方法都是各个操作系统能公共调用的,也就是线程在构建时候,会将这些方法注册上去
 

    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static {
        registerNatives();
    }
  • 3.start0() 会调用 JVM_StartThread 这个方法,JVM层面去启动一个线程

我们需要下载hotspot的源码才能找到,我们找到: jvm.cpp这个文件
JVM_ENTRY是用来定义 JVM_StartThread函数的,在这个函数里面创建了一个真正和平台有关的本地线程

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_StartThread");
...
native_thread = new JavaThread(&thread_entry, sz);

hotspot的源码中 thread.cpp文件中1558行的位置

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();
  _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;
  thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :
                                                     os::java_thread;
  os::create_thread(this, thr_type, stack_sz);
  _safepoint_visible = false;
 
}

这个方法有两个参数,第一个是函数名称,线程创建成功之后会根据这个函数名称调用对应的函数;第二个是当前进程内已经有的线程数量。最后我们重点关注与一下 os::create_thread,实际就是调用平台创建线程的方法来创建线程。

  • 4.其中start方法中会有一个函数调用,os::start_thread(thread);,调用平台启动线程的方法,最终会调用Thread.cpp文件中的JavaThread::run()方法
void Thread::start(Thread* thread) {
  trace("start", thread);
  // Start is different from resume in that its safety is guaranteed by context or
  // being called from a Java method synchronized on the Thread object.
  if (!DisableStartThread) {
    if (thread->is_Java_thread()) {
      // Initialize the thread state to RUNNABLE before starting this thread.
      // Can not set it after the thread started because we do not know the
      // exact thread state at that time. It could be in MONITOR_WAIT or
      // in SLEEPING or some other state.
      java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
                                          java_lang_Thread::RUNNABLE);
    }
    os::start_thread(thread);
  }
}
  • 5.在其中JavaThread会进行一系列的初始化操作,最后有一个方法 thread_main_inner
void JavaThread::run() {
  // initialize thread-local alloc buffer related fields
  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 is now sufficient initialized to be handled by the safepoint code as being
  // in the VM. Change thread state from _thread_new to _thread_in_vm
  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!
}
  • 6.thread_main_inner 中,找到核心的代码块 this->entry_point()(this,this),在第四部中native_thread=newJavaThread(&thread_entry,sz); 的时候传递了一个thread_entry函数
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);
    this->entry_point()(this, this);
  }
  DTRACE_THREAD_PROBE(stop, this);
  this->exit(false);
  delete this;
}
  • 7.我们找到thread_entry函数,找到vmSymbols::run_method_name()这个调用,通过回调方法调用Java线程中定义的run方法,让我们来看看, run_method_name是一个宏定义,在vmSymbols.hpp文件中可以找到
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);
}
  • 8.找到VM_SYMBOLS_DO 函数,我们可以看到,其对应的就是一个,方法,
#define VM_SYMBOLS_DO(template, do_alias)    
...
template(run_method_name, "run")  
...

总结全文

其大概意思就是,Java里面创建线程之后,必须要调用start方法才能创建一个线程,该方法会通过虚拟机启动一个本地线程,本地线程的创建会调用当前系统去创建线程的方法进行创建线程,并且线程被执行的时候会回调 run方法进行业务逻辑的处理,大概意思就是,系统给你留了一个接口,你要去用这个接口调用你的run 方法,该线程提供给你一系列的操作。
 

参考:

https://blog.csdn.net/qq_41259576/article/details/107056383

https://blog.csdn.net/weixin_43490440/article/details/101519957

https://developer.aliyun.com/article/72803

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐