最近面试被问了Thread与runable的原理有什么不同,本人当时回答的是没什么不同,都是开一个新线程而已,面试官也没有给我个正面反馈告诉我到底有什么不同,索性趁着这个热乎劲我就去深入剖析一下这个Thread。首先写一个例子看看Thread和runable分别是怎么用的。(查看源码)
先添加一个Thread的子类,并重新run方法即可
package com.example.threaddemo;
import android.util.Log;
public class ThreadSub extends Thread implements TAG{
@Override
public void run() {
Log.e(TAG, "run: ThreadSub" );
}
}
再添加一个runnable 的实现类,实现他的run方法
package com.example.threaddemo;
import android.util.Log;
public class RunnableImpl implements Runnable,TAG {
String text ="";
private RunnableImpl() {
}
public RunnableImpl(String text) {
this.text = text;
}
@Override
public void run() {
Log.e(TAG, "run: RunableImpl "+text );
try {
Thread.sleep(5000);
}catch (Exception e){
}
}
}
再加一个使用刚刚写好的类的代码
void createThreadWay1() {
new ThreadSub().start();
}
void createThreadWay2() {
new Thread(new RunnableImpl("createThreadWay2")).start();
}
然后点击运行就可以了,实现非常简单,下面就先来看一下Thread是如何在调用start()之后让run()方法运行在新创建的线程里的,这个过程需要jdk源码的协助,这次的学习是基于openjdk8的版本,我下载了一份放到了github上(点这里去下载),接下来就从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 */
}
}
}
这里首先判断了一下Thread的状态,只有当Threadstatus等于0的时候才说明Thread当前处于尚未启动的状态。之后将thread加入所属的线程组,接着就调用了最关键的一步start0(),start0()是本地方法
private native void start0();
找一找该方法native层对应的实现
jdk/src/share/native/java/lang/Thread.c:
{"start0", "()V", (void *)&JVM_StartThread},
最终在jvm.cpp内找到了该实现
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
bool throw_illegal_thread_state = false;
// We must release the Threads_lock before we can post a jvmti event
// in Thread::start.
{
MutexLocker mu(Threads_lock);
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));
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);
}
}
}
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
代码里用了很多的宏,开起来不是很方便,可以利用预编译将宏展开
gcc -E jvm.cpp -I../ -I/home/wk/android/openJdk8/build/linux-x86_64-normal-server-release/hotspot/linux_amd64_compiler2/generated/ -I./ >temp.cpp
宏展开之后
extern "C" {
void JNICALL JVM_StartThread(JNIEnv *env, jobject jthread) {
JavaThread *thread = JavaThread::thread_from_jni_environment(env);
ThreadInVMfromNative __tiv(thread);
HandleMarkCleaner __hm(thread);
Thread *__the_thread__ = thread;
os::verify_stack_alignment();;
JavaThread *native_thread = __null;
bool throw_illegal_thread_state = false;
{
MutexLocker mu(Threads_lock);
if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != __null) {
throw_illegal_thread_state = true;
} 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) {
native_thread->prepare(jthread);
}
}
}
if (throw_illegal_thread_state) {
{
Exceptions::_throw_msg(__the_thread__, "jvm.cpp", 2867,
vmSymbols::java_lang_IllegalThreadStateException(), __null);
return;
};
};
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");
}
{
Exceptions::_throw_msg(__the_thread__, "jvm.cpp", 2881, vmSymbols::java_lang_OutOfMemoryError(),
"unable to create new native thread");
return;
};
}
Thread::start(native_thread);
}
}
这里面new了一个JavaThread,有名字可以联想到这个JavaThread应该是直接关联Java层的Thread的,下面重点关注这个类,先观察一下他的构造函数
JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
Thread()
#if INCLUDE_ALL_GCS
, _satb_mark_queue(&_satb_mark_queue_set),
_dirty_card_queue(&_dirty_card_queue_set)
#endif // INCLUDE_ALL_GCS
{
if (TraceThreadEvents) {
tty->print_cr("creating thread %p", this);
}
initialize();
_jni_attach_state = _not_attaching_via_jni;
set_entry_point(entry_point);
// Create the native thread itself.
// %note runtime_23
os::ThreadType thr_type = os::java_thread;
thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :
os::java_thread;
os::create_thread(this, thr_type, stack_sz);
_safepoint_visible = false;
}
在函数内调用了 os::create_thread
bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
assert(thread->osthread() == NULL, "caller responsible");
// Allocate the OSThread object
OSThread* osthread = new OSThread(NULL, NULL);
if (osthread == NULL) {
return false;
}
// set the correct thread state
osthread->set_thread_type(thr_type);
// Initial state is ALLOCATED but not INITIALIZED
osthread->set_state(ALLOCATED);
thread->set_osthread(osthread);
// init thread attributes
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// stack size
if (os::Linux::supports_variable_stack_size()) {
// calculate stack size if it's not specified by caller
if (stack_size == 0) {
stack_size = os::Linux::default_stack_size(thr_type);
switch (thr_type) {
case os::java_thread:
// Java threads use ThreadStackSize which default value can be
// changed with the flag -Xss
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;
}
}
stack_size = MAX2(stack_size, os::Linux::min_stack_allowed);
pthread_attr_setstacksize(&attr, stack_size);
} else {
// let pthread_create() pick the default value.
}
// glibc guard page
pthread_attr_setguardsize(&attr, os::Linux::default_guard_size(thr_type));
ThreadState state;
{
// Serialize thread creation if we are running with fixed stack LinuxThreads
bool lock = os::Linux::is_LinuxThreads() && !os::Linux::is_floating_stack();
if (lock) {
os::Linux::createThread_lock()->lock_without_safepoint_check();
}
pthread_t tid;
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;
}
// Store pthread info into the OSThread
osthread->set_pthread_id(tid);
// 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);
}
}
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;
}
走到这看到了曙光,在上面函数体内看到了熟悉的 pthread_create,这是在C++中创建线程的接口,原理java的Thread也是调用了 pthread_create的,
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
这里又产生了个问题,这个传入的函数指针java_start 难道对应的就是Thread层的run函数?带着这个疑问开始掉头返航找找这个函数指针是什么,
static void *java_start(Thread *thread) {
static int counter = 0;
int pid = os::current_process_id();
alloca(((pid ^ counter++) & 7) * 128);
ThreadLocalStorage::set_thread(thread);
OSThread* osthread = thread->osthread();
Monitor* sync = osthread->startThread_lock();
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;
}
osthread->set_thread_id(os::Linux::gettid());
if (UseNUMA) {
int lgrp_id = os::numa_get_group_id();
if (lgrp_id != -1) {
thread->set_lgrp_id(lgrp_id);
}
}
os::Linux::hotspot_sigmask(thread);
os::Linux::init_thread_fpu_state();
{
MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
osthread->set_state(INITIALIZED);
sync->notify_all();
while (osthread->get_state() == INITIALIZED) {
sync->wait(Mutex::_no_safepoint_check_flag);
}
}
thread->run();
return 0;
}
这个函数也不短啊,有趣的是这个函数的参数居然是Thread*,而且函数最后也调用了这个Thread的run函数,由于本人是个大菜鸟,还是头一次看见创建线程时候还可以传Thread*,虽然他也是个指针,但是没有强制转成void*就可以直接编译成功,真是长见识了。话说故事发展到这,这个Thread->run应该是个重头戏,继续看
// The first routine called by a new Java thread
void JavaThread::run() {
// initialize thread-local alloc buffer related fields
this->initialize_tlab();
// used to test validitity of stack trace backs
this->record_base_of_stack_pointer();
// Record real stack base and size.
this->record_stack_base_and_size();
// Initialize thread local storage; set before calling MutexLocker
this->initialize_thread_local_storage();
this->create_stack_guard_pages();
this->cache_global_variables();
// Thread is now sufficient initialized to be handled by the safepoint code as being
// in the VM. Change thread state from _thread_new to _thread_in_vm
ThreadStateTransition::transition_and_fence(this, _thread_new, _thread_in_vm);
assert(JavaThread::current() == this, "sanity check");
assert(!Thread::current()->owns_locks(), "sanity check");
DTRACE_THREAD_PROBE(start, this);
// This operation might block. We call that after all safepoint checks for a new thread has
// been completed.
this->set_active_handles(JNIHandleBlock::allocate_block());
if (JvmtiExport::should_post_thread_life()) {
JvmtiExport::post_thread_start(this);
}
EventThreadStart event;
if (event.should_commit()) {
event.set_javalangthread(java_lang_Thread::thread_id(this->threadObj()));
event.commit();
}
// We call another function to do the rest so we are sure that the stack addresses used
// from there will be lower than the stack base just computed
thread_main_inner();
// Note, thread is no longer valid at this point!
}
void JavaThread::thread_main_inner() {
assert(JavaThread::current() == this, "sanity check");
assert(this->threadObj() != NULL, "just checking");
// Execute thread entry point unless this thread has a pending exception
// or has been stopped before starting.
// Note: Due to JVM_StopThread we can have pending exceptions already!
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);
}
DTRACE_THREAD_PROBE(stop, this);
this->exit(false);
delete this;
}
this->entry_point()(this, this);
这个函数指着应该就是直接调用java层Thread 的run了,他是在JavaThread的构造中被赋值的,金身就是这个函数
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);
}
到这里就实现了在新线程内运行了run函数