java.lang之java.lang.Thread源码阅读及分析

首先看下这个方法

/* Make sure registerNatives is the first thing <clinit> does. */

    private static native void registerNatives();
    static {
        registerNatives();
    }
registerNatives是一个,native方法,它的作用在于java代码中可以去调用另一个非java编写的函数。也就是说这个native方法不是在java代码里实现,它可以由其它任一语言去实现。当然实现的时候需要遵守一定的规范,通常我们关注的是hotspot vm里对该方法的实现。引用stackoverflow上的回答:

Normally, in order for the JVM to find your native functions, they have to be named a certain way. e.g., for java.lang.Object.registerNatives, the corresponding C function is named Java_java_lang_Object_registerNatives. By using registerNatives (or rather, the JNI function RegisterNatives), you can name your C functions whatever you want.


所以在hotspotvm中,registerNatives对应的C语言函数名叫 Java_java_lang_Thread_registerNatives, 见Thread.c

上面说了使用native方法是为了去调用非java编写的函数,在hotspotvm里,调用的函数如下:

 
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数组中,建立了java函数与c函数指针的对应关系。比如,Thread类中调用sleep方法时会去调用 jvm.cpp 中的 JVM_Sleep方法,

接下来我们先看下Thread类主要的几个成员:

	private char        name[];//线程名称
    private int         priority;//优先级
    private boolean     daemon = false;//默认非守护线程,守护线程与非守护线程的区别在于如果还有非守护线程在运行,那么程序就不会终止
    private Runnable target;//目标任务
    private ThreadGroup group;//所属哪个组,在初始化的时候会分配线程组
    private long stackSize;//栈大小
    private long tid;//线程id
    private volatile int threadStatus = 0;//线程状态
    volatile Object parkBlocker;//线程被谁阻塞
    private volatile Interruptible blocker;//用于真正打断线程
    private final Object blockerLock = new Object();
    public final static int MIN_PRIORITY = 1;
    public final static int NORM_PRIORITY = 5;
    public final static int MAX_PRIORITY = 10;


下面我们来看下常用到的几个方法

1,构造函数

Thread的构造函数中进行了相关的初始化,具体在init函数中,其初始化步骤主要有:

a,设置线程组,默认和当前线程的组一样

b,threadGroup中记录的nUnstartedThreads加1

c,设置tartget,statckSIze,priority等成员的初始值

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

start方法是一个同步方法,首先会检查状态,如果状态不为0(NEW),抛异常,所以如果多次调用同一个线程的start方法时,只有一次能够成功,其他情况下都会抛异常.然后将该线程对象加入到threadGroup中,threadGroup用一个数组来记录了这个组有哪些线程,紧接着调用本地方法start0,如果调用中有异常,会从threadGroup中移除该thread对象.接下来看下start0,其实现在jvm中,对应JVM_StartThread方法,jvm在java调用start方法时,会创建线程,其主要的实现如下:

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
  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) {
        // Note: the current thread is not being used within "prepare".
        native_thread->prepare(jthread);
      }
    }
  }
  ......
  Thread::start(native_thread);

JVM_END

其中native_thread=new JavaThread(&thread_entry,sz),真正创建线程是在这个方法里,thread_entry是告诉线程该执行方法(即java Thread对象中的run方法),sz为栈大小,这里默认大小传的是0,下面看下new JavaThread都干了些啥,

JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
  Thread()
{
  initialize();
  
  set_entry_point(entry_point);
  .......
  os::create_thread(this, thr_type, stack_sz);
}

initialize进行初始化,这里初始化所做的事情比较多,我挑两个来看下,

a,初始化时,记录thread的当前状态为_thread_new=2,其状态枚举如下,这里表示的是java thread的状态

enum JavaThreadState {
  _thread_uninitialized     =  0, // should never happen (missing initialization)
  _thread_new               =  2, // just starting up, i.e., in process of being initialized
  _thread_new_trans         =  3, // corresponding transition state (not used, included for completness)
  _thread_in_native         =  4, // running in native code
  _thread_in_native_trans   =  5, // corresponding transition state
  _thread_in_vm             =  6, // running in VM
  _thread_in_vm_trans       =  7, // corresponding transition state
  _thread_in_Java           =  8, // running in Java or in stub code
  _thread_in_Java_trans     =  9, // corresponding transition state (not used, included for completness)
  _thread_blocked           = 10, // blocked in vm
  _thread_blocked_trans     = 11, // corresponding transition state
  _thread_max_state         = 12  // maximum thread state+1 - used for statistics allocation
};
b,为线程分配Parker,用于处理线程的park与unpark

接下来就是调用os::create_thread真正创建线程了,主要有以下步骤:

a,分配OsThread,并设置其状态为ALLOCATED,这里的状态是指osthread的状态,其枚举如下:

enum ThreadState {
  ALLOCATED,                    // Memory has been allocated but not initialized
  INITIALIZED,                  // The thread has been initialized but yet started
  RUNNABLE,                     // Has been started and is runnable, but not necessarily running
  MONITOR_WAIT,                 // Waiting on a contended monitor lock
  CONDVAR_WAIT,                 // Waiting on a condition variable
  OBJECT_WAIT,                  // Waiting on an Object.wait() call
  BREAKPOINTED,                 // Suspended at breakpoint
  SLEEPING,                     // Thread.sleep()
  ZOMBIE                        // All done, but not reclaimed yet
};

,然后将javathread与该osthread关联,即一个javathread对应一个osthread,1:1的对应关系.

b,初始化线程属性,分离线程

pthread_attr_t attr;
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

c,设置栈(stack_size)大小

默认传入的stack_size=0,linux下amd64默认的大小是1M,其他为512K

#ifdef AMD64
  size_t s = (thr_type == os::compiler_thread ? 4 * M : 1 * M);
#else
  size_t s = (thr_type == os::compiler_thread ? 2 * M : 512 * K);
#endif // AMD64

d,调用pthread_create函数创建线程
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);

第一个参数表示线程id,第二个参数是线程属性,第三个参数java_start是运行函数入口地址,thread为运行函数的参数,下面看下java_start函数:
static void *java_start(Thread *thread) {
  ....
  OSThread* osthread = thread->osthread();//java thread对应的os thread
  Monitor* sync = osthread->startThread_lock();
  osthread->set_thread_id(os::Linux::gettid());//设置linux线程id
	.....
  {
    MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
    osthread->set_state(INITIALIZED);//设置os thread状态为INITIALIZED
    sync->notify_all();//唤醒父线程,父线程在thread_create时会等待在
	/**
	// 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);
      }
    }
	*/
    // wait until os::start_thread()
    while (osthread->get_state() == INITIALIZED) {//等待父线程调用os::start_thread()
      sync->wait(Mutex::_no_safepoint_check_flag);
    }
  }
  // call one more level start routine
  thread->run();//执行javaThread的run方法(会通过jni调用)

  return 0;
}

再回到jvm_startThread方法中,接下来执行

if (native_thread->osthread() != NULL) {
        // Note: the current thread is not being used within "prepare".
        native_thread->prepare(jthread);
      }
	  。。。。。。
	  Thread::start(native_thread);

prepare方法主要设置线程优先级,将当前线程加入到维护的一个线程列表中,然后start方法,start调用os:start_thread方法
void os::start_thread(Thread* thread) {
  // guard suspend/resume
  MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);
  OSThread* osthread = thread->osthread();
  osthread->set_state(RUNNABLE);
  pd_start_thread(thread);
}

设置当前osthread状态为RUNNABLE,然后进入pd_start_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();//看到没,这里就是唤醒刚才的那个执行java_start方法的线程
}

至此,线程start方法就结束了,可以看出,java的线程实际上是依托与c语言创建的线程,java线程对象与c创建的线程之间维护了一个对应关系,然后线程的调度等都交由操作系统处理.

2,sleep方法

sleep是一个native方法,其实现在jvm中JVM_Sleep方法

JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
  JVMWrapper("JVM_Sleep");

  if (millis < 0) {//小于0抛异常
    THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
  }

  if (Thread::is_interrupted (THREAD, true) && !HAS_PENDING_EXCEPTION) {//已经被中断,抛异常
    THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
  }

  // Save current thread state and restore it at the end of this block.
  // And set new thread state to SLEEPING.
  JavaThreadSleepState jtss(thread);
	............
  if (millis == 0) {
    // When ConvertSleepToYield is on, this matches the classic VM implementation of
    // JVM_Sleep. Critical for similar threading behaviour (Win32)
    // It appears that in certain GUI contexts, it may be beneficial to do a short sleep
    // for SOLARIS
    if (ConvertSleepToYield) {
      os::yield();
    } else {
      ThreadState old_state = thread->osthread()->get_state();//获取线程原来的状态
      thread->osthread()->set_state(SLEEPING);//设置状态为SLEEPING
      os::sleep(thread, MinSleepInterval, false);//sleep
      thread->osthread()->set_state(old_state);//设置回原来的状态
    }
  } else {
    ThreadState old_state = thread->osthread()->get_state();
    thread->osthread()->set_state(SLEEPING);
    if (os::sleep(thread, millis, true) == OS_INTRPT) {
      // An asynchronous exception (e.g., ThreadDeathException) could have been thrown on
      // us while we were sleeping. We do not overwrite those.
      if (!HAS_PENDING_EXCEPTION) {
        if (event.should_commit()) {
          event.set_time(millis);
          event.commit();
        }
        THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
      }
    }
    thread->osthread()->set_state(old_state);
  }
  if (event.should_commit()) {
    event.set_time(millis);
    event.commit();
  }
....
JVM_END

可以看出其主要的过程为保存当前java线程对应的osthread状态,然后调用osthread的sleep方法,然后还原回去(如果有中断,处理中断后退出),osthread的sleep方法如下,首先看下可允许中断时怎么执行的

if (interruptible) {
    jlong prevtime = javaTimeNanos();
    for (;;) {
      if (os::is_interrupted(thread, true)) {//如果线程已经被中断,返回
        return OS_INTRPT;
      }
      jlong newtime = javaTimeNanos();
       millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC;
      if(millis <= 0) {//如果sleep时间已到,返回
        return OS_OK;
      }
      prevtime = newtime;
      {
        JavaThread *jt = (JavaThread *) thread;
        ThreadBlockInVM tbivm(jt);
        OSThreadWaitState osts(jt->osthread(), false /* not Object.wait() */);//设置osthread状态为condition_wait(osthread->set_state(CONDVAR_WAIT);)
        jt->set_suspend_equivalent();
        slp->park(millis);//这里才是真正进行睡眠,调用 pthread_cond_timedwait实现
        jt->check_and_wait_while_suspended();
      }
    }
  } 
如果不支持可中断,则不会去判断中断状态

3,yield方法

yield也是一个native方法,它的涵义是让出cpu,让其他线程执行(有可能还是调度到该线程),其实现在JVM_Yiele方法中

void os::yield() {
  sched_yield();//无法看到源代码
}
4,interrupt方法
public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }
最终也是调用interrupt0设置了中断标志位

5,join方法

在当前线程中,如果调用另一个线程的join方法时,当前线程会阻塞直到被调用的线程执行完成后才可以继续执行

public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

可以看到join其实是调用object的wait方法实现的,可参见之前的一边文章Object源码阅读及分析.

 至此,java thread的有关常用的方法已经阅读完,阅读后发现,java的thread其实都是依赖操作系统对线程的操作,那操作系统究竟是如何对线程操作的呢?比如如何调度/切换线程?如何让线程睡眠,挂起等等?后面我会按照我自己的理解,写一个简单的线程调度程序来模拟下操作系统的线程操作!






                
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值