首先看下这个方法
/* 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其实都是依赖操作系统对线程的操作,那操作系统究竟是如何对线程操作的呢?比如如何调度/切换线程?如何让线程睡眠,挂起等等?后面我会按照我自己的理解,写一个简单的线程调度程序来模拟下操作系统的线程操作!