线程启动
线程的启动分为两部分,一部分是jdk,另一部分是jvm。jdk部分负责基本的状态校验,实际的启动过程由jvm完成。
jdk部分
线程启动在jdk的实现很简单,
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 */
}
}
}
起初的时候会判断当前的线程的状态,如果不是NEW则抛出异常。然后将该线程加入到线程组中,之后调用native方法start0()完成线程的启动。
jvm部分
{"start0", "()V", (void *)&JVM_StartThread},
start0在jvm中对应的函数是JVM_StartThread,
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
// We cannot hold the Threads_lock when we throw an exception,
// due to rank ordering issues. Example: we might need to grab the
// Heap_lock while we construct the exception.
bool throw_illegal_thread_state = false;
// We must release the Threads_lock before we can post a jvmti event
// in Thread::start.
{
// Ensure that the C++ Thread and OSThread structures aren't freed before
// we operate.
MutexLocker mu(Threads_lock);
// Since JDK 5 the java.lang.Thread threadStatus is used to prevent
// re-starting an already started thread, so we should usually find
// that the JavaThread is null. However for a JNI attached thread
// there is a small window between the Thread object being created
// (with its JavaThread set) and the update to its threadStatus, so we
// have to check for this
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));
// Allocate the C++ Thread structure and create the native thread. The
// stack size retrieved from java is signed, but the constructor takes
// size_t (an unsigned type), so avoid passing negative values which would
// result in really large stacks.
size_t sz = size > 0 ? (size_t) size : 0;
native_thread = new JavaThread(&thread_entry, sz);
// At this point it may be possible that no osthread was created for the
// JavaThread due to lack of memory. Check for this situation and throw
// an exception if necessary. Eventually we may want to change this so
// that we only grab the lock if the thread was created successfully -
// then we can also do this check and throw the exception in the
// JavaThread constructor.
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
1. start防重入
MutexLocker mu(Threads_lock);
if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
throw_illegal_thread_state = true;
}
为了防止并发问题,首先引入一个mutex锁,这个锁是全局的,也就是说在一定程度上线程的启动是串行的。这个锁的作用范围到Thread::start(native_thread)之前。当锁对象的作用于结束后,会在对象的析构方法中自动释放锁。
如果当前线程成功获取了锁,会通过判断Thread的eetop这个字段来判断当前线程是否已经启动了。大家可能会想为什么不适用threadStatus来判断呢?上面代码的注释给出了原因:
由于jni attached的线程在对象创建和修改threadStatus之间有一个窗口,所以使用threadStataus来防重入不能完全满足要求。
threadStatus的更新是在Thread::start(native_thread)中完成的,不在前面提到的锁的作用范围内,所以使用threadStatus可能存在并发问题。
JavaThread* java_lang_Thread::thread(oop java_thread) {
return (JavaThread*)java_thread->address_field(_eetop_offset);
}
2.设置stacksize
从jdk中Thread的stackSize属性中获取栈大小,如果拿到的stacksize小于0则设置为0
3. 初始化JavaThread
传入的任务入口为静态函数thread_entry。
- 设置thread_entry
- 调用pthread_create创建线程,将tid存储在OSThread中,并且绑定JavaThread和OSThread之间的关系。线程的入口是java_start。
- 当前线程会等待子线程完成初始化操作
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); }
只有等子线程的状态变为非ALLOCATED,父进程才会继续执行。
java_start会进行下面几个操作,
- 通过pthread_sigmask为当前线程屏蔽几个信号,包括SIGILL,SIGSEGV,SIGBUS等
- 通知父进程已经初始化完成,唤醒父进程。然后等待父进程调用start_thread()
// handshaking with parent thread { MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag); // notify parent thread osthread->set_state(INITIALIZED); sync->notify_all(); // wait until os::start_thread() while (osthread->get_state() == INITIALIZED) { sync->wait(Mutex::_no_safepoint_check_flag); } }
- 等父进程调用start_thread()后,执行thread->run(),实际调用的是thread_entry。
4.开始执行任务
Thread::start(native_thread)
- 更新threadStatus,该状态用于防止并发start
- 唤起被阻塞的子线程
Monitor* sync_with_child = osthread->startThread_lock(); MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag); sync_with_child->notify();
到这里线程的启动流程基本上完成。
线程interrupt
在jvm中对应JVM_Interrupt,
JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_Interrupt");
// Ensure that the C++ Thread and OSThread structures aren't freed before we operate
oop java_thread = JNIHandles::resolve_non_null(jthread);
MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock);
// We need to re-resolve the java_thread, since a GC might have happened during the
// acquire of the lock
JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
if (thr != NULL) {
Thread::interrupt(thr);
}
JVM_END
之后会调用os::interrupt,
void os::interrupt(Thread* thread) {
assert(Thread::current() == thread || Threads_lock->owned_by_self(),
"possibility of dangling Thread pointer");
OSThread* osthread = thread->osthread();
if (!osthread->interrupted()) {
osthread->set_interrupted(true);
// More than one thread can get here with the same value of osthread,
// resulting in multiple notifications. We do, however, want the store
// to interrupted() to be visible to other threads before we execute unpark().
OrderAccess::fence();
ParkEvent * const slp = thread->_SleepEvent ;
if (slp != NULL) slp->unpark() ;
}
// For JSR166. Unpark even if interrupt status already was set
if (thread->is_Java_thread())
((JavaThread*)thread)->parker()->unpark();
ParkEvent * ev = thread->_ParkEvent ;
if (ev != NULL) ev->unpark() ;
}
可以看到如果当前线程没有被interrupt,则设置中断标志位。同时线程的_SleepEvent不为空,则调用_SleepEvent->unpark()中断线程。如果是java线程,则调用parker的unpark方法中断线程。
当调用Thread.Sleep()的时候线程通过_SleepEvent->park()挂起, 当调用Object.wait()的时候线程通过_ParkEvent->park()挂起。
线程Sleep
在jvm中对应JVM_Sleep,
JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
JVMWrapper("JVM_Sleep");
if (millis < 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);
#ifndef USDT2
HS_DTRACE_PROBE1(hotspot, thread__sleep__begin, millis);
#else /* USDT2 */
HOTSPOT_THREAD_SLEEP_BEGIN(
millis);
#endif /* USDT2 */
EventThreadSleep event;
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);
os::sleep(thread, MinSleepInterval, false);
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();
}
#ifndef USDT2
HS_DTRACE_PROBE1(hotspot, thread__sleep__end,1);
#else /* USDT2 */
HOTSPOT_THREAD_SLEEP_END(
1);
#endif /* USDT2 */
// TODO-FIXME: THROW_MSG returns which means we will not call set_state()
// to properly restore the thread state. That's likely wrong.
THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
}
}
thread->osthread()->set_state(old_state);
}
if (event.should_commit()) {
event.set_time(millis);
event.commit();
}
#ifndef USDT2
HS_DTRACE_PROBE1(hotspot, thread__sleep__end,0);
#else /* USDT2 */
HOTSPOT_THREAD_SLEEP_END(
0);
#endif /* USDT2 */
JVM_END
方法开始进行了状态校验,如果线程已经被interrupted,则抛出异常。否则调用os::sleep()将当前线程变为睡眠状态。os::sleep()的本质是调用_SleepEvent的park方法将当前方法挂起。使用这种方式挂起的线程,可以通过interrupt()方法来中断该线程。
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("interrupt");
}
System.out.println("hahaha");
}
});
t1.start();
t1.interrupt();
上面的代码可以输出interrupt和hahaha