Java线程源码解析之start

概述

Java开发中,会经常使用到多线程,有必要深入了解其实现原理;

创建Thread

java.lang.Thread主要的成员变量如下:

    private char        name[];//线程名称
    private int         priority;//优先级
    private volatile int threadStatus = 0;//线程状态
    private boolean     daemon = false;//是否后台线程
    private Runnable target;//线程执行的逻辑
   //每个线程都有一个ThreadLocalMap的成员变量,类似hashmap
   //有兴趣深入了解的可以阅读文章《ThreadLocal源码阅读》
    ThreadLocal.ThreadLocalMap threadLocals = null;
    private long        eetop;//实际上是个指针,指向JavaThread的地址

创建Thread对象时,实际上调用的是init方法,方法逻辑比较简单,这里就不详细介绍了。

start

我们都知道启动线程要调用start方法,那么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) {
            }
        }
    }

    private native void start0();

可以看到主要的逻辑都是通过native方法star0实现的, 查看Thread.c文件可以知道,它实际上调用的是jvm.cpp文件的JVM_StartThread方法:

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

  bool throw_illegal_thread_state = false;
  {
   // Threads_lock代表在活动线程表上的锁,MutexLocker会调用lock方法上锁
    MutexLocker mu(Threads_lock);
    //实际上是判断java.lang.Thread的eetop,正常情况下,在后续步骤中会赋值,但在此处为0,不指向任何对象;
    //其实在start方法中已经根据threadStatus进行了判断,但是由于创建线程对象和更新threadStatus并不是原子操作,因而再次check
    if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
      throw_illegal_thread_state = true;//状态错误,返回
    } else {
     //在java.lang.Thread的init方法中,可设置stack的大小,此处获取设置的大小;
     //不过通常调用构造函数的时候都不会传入stack大小,size=0
      jlong size =
        java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
      size_t sz = size > 0 ? (size_t) size : 0;
     //在下面的[创建JavaThread]介绍
      native_thread = new JavaThread(&thread_entry, sz);
      if (native_thread->osthread() != NULL) {
        native_thread->prepare(jthread);
      }
    }
  }

  if (throw_illegal_thread_state) {
    THROW(vmSymbols::java_lang_IllegalThreadStateException());
  }

  assert(native_thread != NULL, "Starting null thread?");

  //Java线程实际上是通过系统线程实现的,如果创建系统线程失败,报错;
  //有很多原因会导致该错误:比如内存不足、max user processes设置过小 
  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");
    }
    THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
              "unable to create new native thread");
  }
  //在下面的[创建JavaThread]介绍
  Thread::start(native_thread);

JVM_END

创建JavaThread

JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
  Thread()
#ifndef SERIALGC
  , _satb_mark_queue(&_satb_mark_queue_set),
  _dirty_card_queue(&_dirty_card_queue_set)
#endif // !SERIALGC
{
  if (TraceThreadEvents) {
    tty->print_cr("creating thread %p", this);
  }
  initialize();
  _jni_attach_state = _not_attaching_via_jni;
  set_entry_point(entry_point);
 
  os::ThreadType thr_type = os::java_thread;
  //根据entry_point判断是CompilerThread还是JavaThread
  //由于此处传入的为&thread_entry,因此为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);
  _safepoint_visible = false;
}

可以看到JavaThread继承自Thread,而Thread重载了operator new:

public:
  void* operator new(size_t size) { return allocate(size, true); }
  void* operator new(size_t size, std::nothrow_t& nothrow_constant) { return allocate(size, false); }
  void  operator delete(void* p);

 protected:
   static void* allocate(size_t size, bool throw_excpt, MEMFLAGS flags = mtThread);

关于operator new描述如下:

operator new可以做为常规函数被调用;在C ++中,new是一个具有特定行为的操作符:它首先调用operator new函数,用其类型说明符的大小作为第一个参数,如果调用成功,则自动初始化或构造对象。

allocate定义如下:

void* Thread::allocate(size_t size, bool throw_excpt, MEMFLAGS flags) {
  if (UseBiasedLocking) {
  //alignment=2<<10,对于偏向锁,地址要对齐,即低10位为0
  //根据偏向锁的实现,要求线程指向地址的低10位为0,那么该如何实现呢?
  //可以看到此处在申请内存的时候,对原申请大小做了调整;
  //假设申请到到地址为0xA11CA(低10位为0111001010),则0xA2000是满足条件的地址,这两地址间相差0x0E36(小于1<<10),
  //也就是说原申请内存大小+alignment,则可以指针后移,找到符合条件的地址;
  //那为什么要减去sizeof(intptr_t)?因为指针后移最多为alignment-1,即可找到满足条件的地址;
  //而第一位存储的是_real_malloc_address,占用内存空间为sizeof(intptr_t)
  //sizeof(intptr_t)是为了跨平台定义的类型,在64位平台下为8bytes,32位平台为4bytes;
    const int alignment = markOopDesc::biased_lock_alignment;
    size_t aligned_size = size + (alignment - sizeof(intptr_t));
   //throw_excpt传入为true
    void* real_malloc_addr = throw_excpt? AllocateHeap(aligned_size, flags, CURRENT_PC)
                                          : os::malloc(aligned_size, flags, CURRENT_PC);
    void* aligned_addr     = (void*) align_size_up((intptr_t) real_malloc_addr, alignment);
    assert(((uintptr_t) aligned_addr + (uintptr_t) size) <=
           ((uintptr_t) real_malloc_addr + (uintptr_t) aligned_size),
           "JavaThread alignment code overflowed allocated storage");
    if (TraceBiasedLocking) {
      if (aligned_addr != real_malloc_addr)
        tty->print_cr("Aligned thread " INTPTR_FORMAT " to " INTPTR_FORMAT,
                      real_malloc_addr, aligned_addr);
    }
    ((Thread*) aligned_addr)->_real_malloc_address = real_malloc_addr;
    return aligned_addr;
  } else {
    return throw_excpt? AllocateHeap(size, flags, CURRENT_PC)
                       : os::malloc(size, flags, CURRENT_PC);
  }
}
//alignment-1,再取反,即变成0xfffffffffffff800(低10位为0),该方法的效果相当于将低10位变为0
#define align_size_up_(size, alignment) (((size) + ((alignment) - 1)) & ~((alignment) - 1))

inline intptr_t align_size_up(intptr_t size, intptr_t alignment) {
  return align_size_up_(size, alignment);
}

initialize主要是做各种初始化,这边就不详细介绍了;
JavaThred中有几个成员变量比较重要:

 //用于synchronized同步块和Object.wait()
 ParkEvent * _ParkEvent ;  
 //用于Thread.sleep()
  ParkEvent * _SleepEvent ;
 //用于unsafe.park()/unpark(),供java.util.concurrent.locks.LockSupport调用,
 //因此它支持了java.util.concurrent的各种锁、条件变量等线程同步操作,是concurrent的实现基础
  Parker*    _parker;

os::create_thread方法逻辑如下:

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);
  //linux下可以通过pthread_attr_t来设置线程属性
  pthread_attr_t attr;
  pthread_attr_init(&attr);//linux系统调用,更多内容请参考内核文档
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

  // 线程栈大小
  if (os::Linux::supports_variable_stack_size()) {//是否支持设置栈大小
    //如果用户创建线程时未指定栈大小,对于JavaThread会看是否设置了-Xss或ThreadStackSize;
    //如果未设置,则采用系统默认值。对于64位操作系统,默认为1M;
    //操作系统栈大小(ulimit -s):这个配置只影响进程的初始线程;后续用pthread_create创建的线程都可以指定栈大小。
    //HotSpot VM为了能精确控制Java线程的栈大小,特意不使用进程的初始线程(primordial thread)作为Java线程
    if (stack_size == 0) {
      stack_size = os::Linux::default_stack_size(thr_type);
      switch (thr_type) {
      case os::java_thread:
      //读取Xss和ThreadStackSize
        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;
      }
    }
   //栈最小为48k
    stack_size = MAX2(stack_size, os::Linux::min_stack_allowed);
    pthread_attr_setstacksize(&attr, stack_size);
  } else {
    // let pthread_create() pick the default value.
  }

  pthread_attr_setguardsize(&attr, os::Linux::default_guard_size(thr_type));

  ThreadState state;
  {
   //如果linux线程而且不支持设置栈大小,则先获取创建线程锁,获取锁之后再创建线程
    bool lock = os::Linux::is_LinuxThreads() && !os::Linux::is_floating_stack();
    if (lock) {
      os::Linux::createThread_lock()->lock_without_safepoint_check();
    }

    pthread_t tid;
    //调用linux的pthread_create创建线程,传入4个参数
    //第一个参数:指向线程标示符pthread_t的指针;
    //第二个参数:设置线程的属性
    //第三个参数:线程运行函数的起始地址
    //第四个参数:运行函数的参数
    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;
    }

    // 将pthread id保存到osthread
    osthread->set_pthread_id(tid);

    // 等待pthread_create创建的子线程完成初始化或放弃
    {
      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;
}

创建线程时传入了java_start,做为线程运行函数的初始地址:

static void *java_start(Thread *thread) {
  static int counter = 0;
  int pid = os::current_process_id();
 //alloca是用来分配存储空间的,它和malloc的区别是它是在当前函数的栈上分配存储空间,而不是在堆中。
 //其优点是:当函数返回时,自动释放它所使用的栈。
  alloca(((pid ^ counter++) & 7) * 128);

  ThreadLocalStorage::set_thread(thread);

  OSThread* osthread = thread->osthread();
  Monitor* sync = osthread->startThread_lock();

  // non floating stack LinuxThreads needs extra check, see above
  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;
  }

  // thread_id is kernel thread id (similar to Solaris LWP id)
  osthread->set_thread_id(os::Linux::gettid());

 //优先尝试在请求线程当前所处的CPU的Local内存上分配空间。
 //如果local内存不足,优先淘汰local内存中无用的Page
  if (UseNUMA) {//默认为false
    int lgrp_id = os::numa_get_group_id();
    if (lgrp_id != -1) {
      thread->set_lgrp_id(lgrp_id);
    }
  }
  // 调用pthread_sigmask初始化signal mask:VM线程处理BREAK_SIGNAL信号
  os::Linux::hotspot_sigmask(thread);

  // initialize floating point control register
  os::Linux::init_thread_fpu_state();

  // handshaking with parent thread
  {
    MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);

    // 设置状态会INITIALIZED,并通过notify_all唤醒父线程
    osthread->set_state(INITIALIZED);
    sync->notify_all();

    // 一直等待父线程调用 os::start_thread()
    while (osthread->get_state() == INITIALIZED) {
      sync->wait(Mutex::_no_safepoint_check_flag);
    }
  }

  // call one more level start routine
  thread->run();

  return 0;
}

当子线程完成初始化,将状态设置为NITIALIZED并唤醒父线程之后,父线程会执行Thread::start方法:

void Thread::start(Thread* thread) {
  trace("start", thread);
  if (!DisableStartThread) {
    if (thread->is_Java_thread()) {//设置线程状态为RUNNABLE
      java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
                                          java_lang_Thread::RUNNABLE);
    }
    os::start_thread(thread);
  }
}
void os::start_thread(Thread* thread) {
  MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);
  OSThread* osthread = thread->osthread();
//设置线程状态为RUNNABLE, 子线程可以开始执行thread->run()
  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->run();
}

父线程的thread->run主要逻辑为调用thread_main_inner,源码如下:

 void JavaThread::thread_main_inner() {//删除部分非关键代码
  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);
  }
  this->exit(false);
  delete this;
}

那这儿的entry_point是什么呢?实际上在创建JavaThread时,会传入entrypoint:

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

可以看到实际上就是调用java.lang.Thread的run方法;

另外当run方法执行结束,会调用JavaThread::exit方法清理资源

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,577评论 18 399
  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,438评论 1 15
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,598评论 18 139
  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 2,949评论 1 18
  • 1、拒绝购买旅游纪念品; 2、至少保留一层书架或抽屉是空的; 3、尽量减少台面上的物品,不是每天都用的东西收起来,...
    企鹅的北极星阅读 706评论 0 0