Thread、ThreadGroup ThreadLocal以及ThreadGroupContext是线程相关的Java知识,为什么会开这个题是因为在上一章的时候出现ThreadGroupContext这个东西,想对他进行学习并且能实践到实际的应用场景中。
目录
1.Thread
1.1 简述Thread
Thread就是线程,线程解释我觉得可以从外到内,外是就是一台电脑,我们的要使用我们电脑大部分人都 是先具备一个基本的操作系统,比如windows、linux、macos,我们通过操作系统看视频、上微信,其实可以看成称看视频以及上微信是两个应用任务,操作系统通过CPU调度任务执行,让我们看起来可以同时看视频跟上微信,一个任务就是一个进程,而线程就是进程的子单位,相当于你用用视频软件看视频是一个线程,你用视频软件发视频评论是一个线程,你可以在同一个视频软件同时看视频跟发表评论。
在Java里面,一个Java程序会开启一个JVM进程,JVM它是由软件技术模拟出计算机运行的一个虚拟的计算机。我们都知道Java的程序需要经过编译后,产生.Class字节码文件,JVM才能识别并运行它,JVM针对每个操作系统开发其对应的解释器,所以只要其操作系统有对应版本的JVM,那么这份Java编译后的代码就能够运行起来,这就是Java能一次编译,到处运行的原因。
JVM结构如下图所示
JVM 线程共享区: 堆、方法区
JVM 线程独占区:虚拟机栈、本地方法栈、程序计数器
1.2 Thread 基础操作
在Java里面,我们用基础的Java程序说明Thread的应用。
log("主线程执行开始");
new Thread(() -> {
try{
Thread.sleep(300);
}catch (InterruptedException e){
e.printStackTrace();
}
log("运行任务1结束...");
}).start();
new Thread(() -> {
try{
Thread.sleep(200);
}catch (InterruptedException e){
e.printStackTrace();
}
log("运行任务2结束...");
}).start();
log("主线程结束");
控制台输出结果如下:
上面程序是在主线程,开始了两个子线程任务1跟任务2,任务1主要休眠300ms,任务2休眠200ms,从控制台输出结果来看,主线程的结束并不会影响子线程1,2的执行,子线程各自执行也是独立的并不是串联执行,在Java程序中,多线程的应用十分地广泛,让java程序更快地处理任务,达到用户需求。
1.3 Thread启动原理
1.3.1 thread.start()启动线程
   一个Thread的启动是通过触发Thread.start()的方法
真正启动线程的时候start0()这个方法,在之前是将线程放进ThreadGroup中,也就是我们待会讲的,这里不细说。之后就是修改线程的启动状态。
start0 的定义如下:
private native void start0();
1.3.2 创建底层线程对象
一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。在Thread创建的时候先跑的static方法,registerNatives()
而本地方法 registerNatives 是定义在 Thread.c 文件中的。Thread.c 是个很小的文件,它定义了各个操作系统平台都要用到的关于线程的公用数据和操作,如下:
JNIEXPORT void JNICALL Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
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},
};
Thread.java来源
http://hg.openjdk.java.net/jdk6/jdk6/jdk/file/a049121b193b/src/share/classes/java/lang/Thread.java
找到start0对应调用JVM_StartThread,调用JVM_StartThread,抽取核心实现如下:
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
//省略
native_thread = new JavaThread(&thread_entry, sz);
//省略
}
}
JVM_StartThread创建步骤:
1.申请一个锁 创建一个JavaThread对象如果有异常情况,抛出异常
2.对第二步的JavaThread类,做一个准备操作:thread.cpp$JavaThread::prepare
3.调用Thread::start方法,让一条线程开始运行
jvm.cpp代码来源
http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/tip/src/share/vm/prims/jvm.cpp
JVM_StartThread中创建了一个JavaThread对象,
抽取JavaThread创建核心代码:
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
{
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);
}
thread.cpp代码来源
http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/cf85f331361b/src/share/vm/runtime/thread.cpp#l1566
可以看到JavaThread里面创建了OSThread,OSThread是一个平台相关线程,OSThread由JavaThread对象创建并进行管理。在OSThread创建的过程中,会通过pthread方法来创建一个真正意义上的底层级线程。
1.3.3 运行线程
pthread.create新线程创建后就会从thread_native_entry()开始运行,thread_native_entry()中调用了thread->run() ,run()里面调用thread_main_inner()>entry_point()(this, this),entry_point()返回的其实就是在 new JavaThread(&thread_entry, sz) 时传入的thread_entry。这里就相当于调用了thread_entry(this,this)。thread_entry定义在jvm.cpp中:
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);
}
jvm.cpp代码来源
http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/tip/src/share/vm/prims/jvm.cpp
上面的文字可能很流水,就是大概描述跳转方向,大家可以参考着跳。核心的是上面代码片段中的JavaCall,这个方法就是调用Java线程对象的run()方法,就跑了。
以上就是我们一个线程怎么启动运行的过程。
1.4 Thread理解
相信通过上面对Thread的认识讲解,大家多多少少都能大概知道线程他是什么,他是做什么的,他是怎么做的及其相关原理,下一章将在Thread理解的基础上对ThreadGroup进行学习。
下一篇:Java线程基础-认识并区分Thread ThreadGroup ThreadLocal ThreadGroupContext(二)