天天看點

從應用角度看Android源碼 - Thread 深入剖析

最近面試被問了Thread與runable的原理有什麼不同,本人當時回答的是沒什麼不同,都是開一個新線程而已,面試官也沒有給我個正面回報告訴我到底有什麼不同,索性趁着這個熱乎勁我就去深入剖析一下這個Thread。首先寫一個例子看看Thread和runable分别是怎麼用的。(檢視源碼)

先添加一個Thread的子類,并重新run方法即可

package com.example.threaddemo;

import android.util.Log;

public class ThreadSub extends Thread implements TAG{

    @Override
    public void run() {
        Log.e(TAG, "run: ThreadSub" );


    }
}
           

再添加一個runnable 的實作類,實作他的run方法

package com.example.threaddemo;

import android.util.Log;

public class RunnableImpl implements Runnable,TAG {

    String text ="";

    private RunnableImpl() {
    }

    public RunnableImpl(String text) {
        this.text = text;
    }

    @Override
    public void run() {
        Log.e(TAG, "run: RunableImpl "+text );
        try {
            Thread.sleep(5000);

        }catch (Exception e){

        }

    }
}
           

再加一個使用剛剛寫好的類的代碼

void createThreadWay1() {
        new ThreadSub().start();
    }

    void createThreadWay2() {

        new Thread(new RunnableImpl("createThreadWay2")).start();
    }
           

然後點選運作就可以了,實作非常簡單,下面就先來看一下Thread是如何在調用start()之後讓run()方法運作在新建立的線程裡的,這個過程需要jdk源碼的協助,這次的學習是基于openjdk8的版本,我下載下傳了一份放到了github上(點這裡去下載下傳),接下來就從start()入手

public synchronized void start() {
  
        
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        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 */
            }
        }
    }
           

這裡首先判斷了一下Thread的狀态,隻有當Threadstatus等于0的時候才說明Thread目前處于尚未啟動的狀态。之後将thread加入所屬的線程組,接着就調用了最關鍵的一步start0(),start0()是本地方法

private native void start0();
           

找一找該方法native層對應的實作

jdk/src/share/native/java/lang/Thread.c:   
 {"start0",           "()V",        (void *)&JVM_StartThread},
           

最終在jvm.cpp内找到了該實作

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

  bool throw_illegal_thread_state = false;

  // We must release the Threads_lock before we can post a jvmti event
  // in Thread::start.
  {

    MutexLocker mu(Threads_lock);

    if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
      throw_illegal_thread_state = true;
    } else {
      // We could also check the stillborn flag to see if this thread was already stopped, but
      // for historical reasons we let the thread detect that itself when it starts running

      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) {
        // Note: the current thread is not being used within "prepare".
        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) {
    // No one should hold a reference to the 'native_thread'.
    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
           

代碼裡用了很多的宏,開起來不是很友善,可以利用預編譯将宏展開

gcc -E jvm.cpp -I../ -I/home/wk/android/openJdk8/build/linux-x86_64-normal-server-release/hotspot/linux_amd64_compiler2/generated/ -I./ >temp.cpp
           

宏展開之後

extern "C" {
void JNICALL JVM_StartThread(JNIEnv *env, jobject jthread) {
    JavaThread *thread = JavaThread::thread_from_jni_environment(env);
    ThreadInVMfromNative __tiv(thread);
    HandleMarkCleaner __hm(thread);
    Thread *__the_thread__ = thread;
    os::verify_stack_alignment();;
    JavaThread *native_thread = __null;

    bool throw_illegal_thread_state = false;
    {
        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));

            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);
            }
        }
    }

    if (throw_illegal_thread_state) {
        {
            Exceptions::_throw_msg(__the_thread__, "jvm.cpp", 2867,
                                   vmSymbols::java_lang_IllegalThreadStateException(), __null);
            return;
        };
    };

    if (native_thread->osthread() == __null) {

        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");
        }
        {
            Exceptions::_throw_msg(__the_thread__, "jvm.cpp", 2881, vmSymbols::java_lang_OutOfMemoryError(),
                                   "unable to create new native thread");
            return;
        };
    }

    Thread::start(native_thread);
}
}
           

這裡面new了一個JavaThread,有名字可以聯想到這個JavaThread應該是直接關聯Java層的Thread的,下面重點關注這個類,先觀察一下他的構造函數

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

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

  // Allocate the OSThread object
  OSThread* osthread = new OSThread(NULL, NULL);
  if (osthread == NULL) {
    return false;
  }

  // set the correct thread state
  osthread->set_thread_type(thr_type);

  // Initial state is ALLOCATED but not INITIALIZED
  osthread->set_state(ALLOCATED);

  thread->set_osthread(osthread);

  // init thread attributes
  pthread_attr_t attr;
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

  // stack size
  if (os::Linux::supports_variable_stack_size()) {
    // calculate stack size if it's not specified by caller
    if (stack_size == 0) {
      stack_size = os::Linux::default_stack_size(thr_type);

      switch (thr_type) {
      case os::java_thread:
        // Java threads use ThreadStackSize which default value can be
        // changed with the flag -Xss
        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;
        } // else fall through:
          // use VMThreadStackSize if CompilerThreadStackSize is not defined
      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 {
    // let pthread_create() pick the default value.
  }

  // glibc guard page
  pthread_attr_setguardsize(&attr, os::Linux::default_guard_size(thr_type));

  ThreadState state;

  {
    // Serialize thread creation if we are running with fixed stack LinuxThreads
    bool lock = os::Linux::is_LinuxThreads() && !os::Linux::is_floating_stack();
    if (lock) {
      os::Linux::createThread_lock()->lock_without_safepoint_check();
    }

    pthread_t tid;
    int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);

    pthread_attr_destroy(&attr);

    if (ret != 0) {
      if (PrintMiscellaneous && (Verbose || WizardMode)) {
        perror("pthread_create()");
      }
      // Need to clean up stuff we've allocated so far
      thread->set_osthread(NULL);
      delete osthread;
      if (lock) os::Linux::createThread_lock()->unlock();
      return false;
    }

    // Store pthread info into the OSThread
    osthread->set_pthread_id(tid);

    // Wait until child thread is either initialized or aborted
    {
      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();
    }
  }

  // Aborted due to thread limit being reached
  if (state == ZOMBIE) {
      thread->set_osthread(NULL);
      delete osthread;
      return false;
  }

  // The thread is returned suspended (in state INITIALIZED),
  // and is started higher up in the call chain
  assert(state == INITIALIZED, "race condition");
  return true;
}
           

走到這看到了曙光,在上面函數體内看到了熟悉的 pthread_create,這是在C++中建立線程的接口,原理java的Thread也是調用了 pthread_create的,

int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
           

這裡又産生了個問題,這個傳入的函數指針java_start 難道對應的就是Thread層的run函數?帶着這個疑問開始掉頭返航找找這個函數指針是什麼,

static void *java_start(Thread *thread) {
  static int counter = 0;
  int pid = os::current_process_id();
  alloca(((pid ^ counter++) & 7) * 128);

  ThreadLocalStorage::set_thread(thread);

  OSThread* osthread = thread->osthread();
  Monitor* sync = osthread->startThread_lock();
  if (!_thread_safety_check(thread)) {
    // notify parent thread
    MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
    osthread->set_state(ZOMBIE);
    sync->notify_all();
    return NULL;
  }

  osthread->set_thread_id(os::Linux::gettid());

  if (UseNUMA) {
    int lgrp_id = os::numa_get_group_id();
    if (lgrp_id != -1) {
      thread->set_lgrp_id(lgrp_id);
    }
  }
  os::Linux::hotspot_sigmask(thread);
  os::Linux::init_thread_fpu_state();
  {
    MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
    osthread->set_state(INITIALIZED);
    sync->notify_all();
    while (osthread->get_state() == INITIALIZED) {
      sync->wait(Mutex::_no_safepoint_check_flag);
    }
  }
  thread->run();
  return 0;
}
           

這個函數也不短啊,有趣的是這個函數的參數居然是Thread*,而且函數最後也調用了這個Thread的run函數,由于本人是個大菜鳥,還是頭一次看見建立線程時候還可以傳Thread*,雖然他也是個指針,但是沒有強制轉成void*就可以直接編譯成功,真是長見識了。話說故事發展到這,這個Thread->run應該是個重頭戲,繼續看

// The first routine called by a new Java thread
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!
}
           
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;
}

           
this->entry_point()(this, this);
           

這個函數指着應該就是直接調用java層Thread 的run了,他是在JavaThread的構造中被指派的,金身就是這個函數

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);
}
           

到這裡就實作了在新線程内運作了run函數

繼續閱讀