一,线程的创建
在java代码中可以有多种方式创建一个线程,常用的方法是:
Thread thd = new Thread();
这个创建会调用init,init2初始化一个线程:
libcore/ojluni/src/main/java/java/lang/Thread.java
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
Thread parent = currentThread();
...
init2(parent);
tid = nextThreadID();
}
private void init2(Thread parent) {
this.contextClassLoader = parent.getContextClassLoader();
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(
parent.inheritableThreadLocals);
}
new 一个Thread对象,其实新线程并没有被创建出来,而是要等到Thread.start()被调用才会新起线程。
public synchronized void start() {
started = false;
try {
nativeCreate(this, stackSize, daemon);
started = true;
} finally {
}
}
通过nativeCreate(),java层的Thread类最终关联的是native层中的Thread.cc
art/runtime/Thread.cc
void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) {
CHECK(java_peer != nullptr);
Thread* self = static_cast<JNIEnvExt*>(env)->self;
Runtime* runtime = Runtime::Current();
// Atomically start the birth of the thread ensuring the runtime isn't shutting down.
bool thread_start_during_shutdown = false;
{
MutexLock mu(self, *Locks::runtime_shutdown_lock_);
if (runtime->IsShuttingDownLocked()) {
thread_start_during_shutdown = true;
} else {
runtime->StartThreadBirth();
}
}
Thread* child_thread = new Thread(is_daemon);
// Try to allocate a JNIEnvExt for the thread. We do this here as we might be out of memory and
// do not have a good way to report this on the child's side.
std::unique_ptr<JNIEnvExt> child_jni_env_ext(
JNIEnvExt::Create(child_thread, Runtime::Current()->GetJavaVM()));
int pthread_create_result = 0;
if (child_jni_env_ext.get() != nullptr) {
pthread_t new_pthread;
pthread_attr_t attr;
child_thread->tlsPtr_.tmp_jni_env = child_jni_env_ext.get();
CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), "new thread");
CHECK_PTHREAD_CALL(pthread_attr_setdetachstate, (&attr, PTHREAD_CREATE_DETACHED),
"PTHREAD_CREATE_DETACHED");
CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, stack_size), stack_size);
pthread_create_result = pthread_create(&new_pthread,
&attr,
Thread::CreateCallback,
child_thread);
CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), "new thread");
}
// Either JNIEnvExt::Create or pthread_create(3) failed, so clean up.
{
MutexLock mu(self, *Locks::runtime_shutdown_lock_);
runtime->EndThreadBirth();
}
}
StartThreadBirth,EndThreadBirth是配套使用的,表示当前有新线程在孵化中。
接下来通过Thread* child_thread = new Thread(is_daemon);创建一个新的线程类,但是真正fork一个线程的地方,还是系统调用pthread_create函数。
bionic/libc/bionic/Pthread_create.cpp
int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr,
void* (*start_routine)(void*), void* arg) {
}
其中第一个参数是指向线程标志符的指针;第二个参数线程的属性值;第三个参数是新线程的入口地址;第四个参数是新线程入口函数所需的参数。
art/runtime/Thread.cc
void* Thread::CreateCallback(void* arg) {
Thread* self = reinterpret_cast<Thread*>(arg);
Runtime* runtime = Runtime::Current();
{
// Note: given that the JNIEnv is created in the parent thread, the only failure point here is
// a mess in InitStackHwm. We do not have a reasonable way to recover from that, so abort
// the runtime in such a case. In case this ever changes, we need to make sure here to
// delete the tmp_jni_env, as we own it at this point.
CHECK(self->Init(runtime->GetThreadList(), runtime->GetJavaVM(), self->tlsPtr_.tmp_jni_env));
self->tlsPtr_.tmp_jni_env = nullptr;
Runtime::Current()->EndThreadBirth();
}
{
// Invoke the 'run' method of our java.lang.Thread.
mirror::Object* receiver = self->tlsPtr_.opeer;
jmethodID mid = WellKnownClasses::java_lang_Thread_run;
ScopedLocalRef<jobject> ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
InvokeVirtualOrInterfaceWithJValues(soa, ref.get(), mid, nullptr);
}
// Detach and delete self.
Runtime::Current()->GetThreadList()->Unregister(self);
return nullptr;
}
其中的参数arg就是CreateNativeThread()中的创建的Thread类对象,CreateCallback这个函数执行完,一个新的线程就起来了。
这个函数主要做了那些事情?
1)self->init的调用,将其纳入虚拟机的统一管理。
2)准备函数InvokeVirtualOrInterfaceWithJValues需要的参数,其中的参数mid表示java函数的ID号,这里的赋值是:jmethodID mid = WellKnownClasses::java_lang_Thread_run;也就是Thread的run方法。WellKnownClasses这个类是一个基础类,他会通过进一步的调用通过ClassLinker来完成函数的查找。
3)执行新线程承载任务的代码,也就是在native层调用并执行java层中的run函数,这个CreateCallBack是怎么找到run函数对应的代码的?这就是InvokeVirtualOrInterfaceWithJValues完成的工作。
art/runtime/Reflection.cc
JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa,
jobject obj, jmethodID mid, jvalue* args) {
mirror::Object* receiver = soa.Decode<mirror::Object*>(obj);
ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
if (is_string_init) {
// Replace calls to String.<init> with equivalent StringFactory call.
method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
receiver = nullptr;
}
uint32_t shorty_len = 0;
const char* shorty = method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty(&shorty_len);
JValue result;
ArgArray arg_array(shorty, shorty_len);
arg_array.BuildArgArrayFromJValues(soa, receiver, args);
InvokeWithArgArray(soa, method, &arg_array, &result, shorty);
return result;
}
通过FindVirtualMethod查找到正确的artMethod,ArtMethod是虚拟机内部对函数的描述。然后调用InvokeWithArgArray执行查找到的函数。
二,线程的挂起
比如在执行GC时,通常需要把所有线程挂起。
art/runtime/Thread_list.cc
void ThreadList::SuspendAll(const char* cause, bool long_suspend) {
Thread* self = Thread::Current();
{
ScopedTrace trace("Suspending mutator threads");
const uint64_t start_time = NanoTime();
SuspendAllInternal(self, self);
// All threads are known to have suspended (but a thread may still own the mutator lock)
// Make sure this thread grabs exclusive access to the mutator lock and its protected data.
#if HAVE_TIMED_RWLOCK
while (true) {
if (Locks::mutator_lock_->ExclusiveLockWithTimeout(self, kThreadSuspendTimeoutMs, 0)) {
break;
} else if (!long_suspend_) {
// Reading long_suspend without the mutator lock is slightly racy, in some rare cases, this
// could result in a thread suspend timeout.
// Timeout if we wait more than kThreadSuspendTimeoutMs seconds.
UnsafeLogFatalForThreadSuspendAllTimeout();
}
}
#else
Locks::mutator_lock_->ExclusiveLock(self);
#endif
long_suspend_ = long_suspend;
const uint64_t end_time = NanoTime();
const uint64_t suspend_time = end_time - start_time;
suspend_all_historam_.AdjustAndAddValue(suspend_time);
}
}
Thread_list是当前虚拟机中管理的线程的集合,SuspendAllInternal会挂起所有运行着的线程,没有运行的线程不要启动。
做法是给各个线程设置suspend-reques flag使其进入挂起状态,也即是kSuspendRequest,这个判断线程是否挂起的函数中一个参考值。
对线程挂起的管理主要利用mutator_lock控制,这是一个读写锁。