@Override
public void run() {
if (target != null) {
target.run();
}
}
再Ctrl + Alt + 鼠标左键,定位前面测试类MyThread中重写的run()方法
可以发现,直接就调用到重写的run()方法了,并且,你可以重复调用多次,而且每一次都是由main主线程调用run()方法,然后非main主线程,执行run()方法代码块
虽然run()方法也可以直接执行到重写的代码块,不过实际当中,按规范来说,不会直接调用run()执行,而是通过start()方法来开启线程,start()方法底层会判断线程的状态,开启线程,最终调用run()方法并结束线程状态,往后了解。
2.2 start()方法
Oracle JDK源码stat()
方法:
public class Thread implements Runnable {
...
private ThreadGroup group;
private volatile int threadStatus = 0;
...
public synchronized void start() {
//如果线程状态不是0-新建,则抛异常
if (threadStatus != 0)
throw new IllegalThreadStateException();
//将当前线程添加到线程组
group.add(this);
//线程是否开启,默认false-未开启
boolean started = false;
try {
//调用native本地方法(最终调用run()方法的核心入口)
start0();
//执行完start0,说明线程已正常启动
started = true;
} finally {
try {
if (!started) {
//线程启动失败,则从线程组中将该线程删除
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
...
}
}
}
难点和重点是调用的start0()
方法,这是一个native(本地方法/底层方法),源码:
//native本地方法,底层开启异步线程,由JVM创建并启动线程,并最终调用run()方法
private native void start0();
由于Oracle JDK对于native本地方法的源码隐藏了具体实现,可以去Open JDK官方或Github下载源码
以下载open jdk8最新的源码版本jdk8u60为例,需要下载jdk
和hotspot
两种源码来研究
- 下载
jdk8u60
源码
http://hg.openjdk.java.net/jdk8u/jdk8u60/jdk/file/935758609767
- 下载jdk8u60的
hotspot
源码
http://hg.openjdk.java.net/jdk8u/jdk8u60/hotspot/file/37240c1019fd
最后发现Open JDK官网下载实在是太慢了,而且还需要分别下载jdk8u60和jdk8u60_hotspot的源码
不过现在OpenJDK的源码已经放到GitHub,可以直接去GitHub下载,而且jdk源码和hotspot源码直接放一起了,省事,这里以下载jdk8-b60
示例,下载地址:
https://github.com/openjdk/jdk/releases/tag/jdk8-b60
下载完解压,主要关注jdk和hotspot这两个源码目录:
使用相关工具,比如Scource Insight
查看底层方法native stat0
具体源码实现
(教程)安装Scource Insight
(教程)使用Scource Insight打开OpenJDK源码
接下来通过Scource Insight查看底层方法start0是如何一步步,最终由主线程调用Thread#run()方法的源码分析过程
首先查看
源码文件:\jdk-jdk8-b60\jdk\src\share\native\java\lang\ Thread.c
关键源码:JVM_StartThread
static JNINativeMethod methods[] = {
{"start0", "()V", (void \*)&JVM_StartThread},//JVM开启线程的主入口
{"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},
};
源码文件:\jdk-jdk8-b60\hotspot\src\share\vm\prims\ jvm.cpp
关键源码:new JavaThread
JVM\_ENTRY(void, JVM\_StartThread(JNIEnv\* env, jobject jthread))
JVMWrapper("JVM\_StartThread");
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) {
THROW(vmSymbols::java\_lang\_IllegalThreadStateException());
}
assert(native_thread != NULL, "Starting null thread?");
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");
}
THROW\_MSG(vmSymbols::java\_lang\_OutOfMemoryError(),
"unable to create new native thread");
}
Thread::start(native_thread);
JVM_END
源码文件:\jdk-jdk8-b60\hotspot\src\share\vm\prims\ jvm.cpp
关键源码:set_thread_status*RUNNABLE
**
void Thread::start(Thread\* thread) {
trace("start", thread);
if (!DisableStartThread) {
if (thread->is\_Java\_thread()) {
java_lang_Thread::set\_thread\_status(((JavaThread\*)thread)->threadObj(),
java_lang_Thread::RUNNABLE);//设置线程状态为RUNNABLE-运行状态
}
os::start\_thread(thread);
}
}
源码文件:\jdk-jdk8-b60\hotspot\src\share\vm\runtime\ os.cpp
关键源码:osthread->set_state(RUNNABLE
)
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);//设置线程状态为RUNNABLE-运行状态, 子线程可以开始执行thread->run()
pd\_start\_thread(thread);
}
源码文件:\jdk-jdk8-b60\hotspot\src\os\linux\vm\ os_linux.cpp
关键源码:sync_with_child->notify()
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);
//这里就是最终父线程调用thread->run()方法的主入口
sync_with_child->notify();
}
所以调用线程的start()方法,底层主要执行过程为:
三、使用总结
run()和start()方法的区别?
- 调用run()方法和执行重写run()方法的代码块都是由main主线程来完成的;
- 调用start()方法是由main主线程来调用,但是重写的run()方法代码块是由
非main
主线程来执行的。- 除此之外,start()方法JDK底层Thread源码的start()方法中,会判断线程状态是否是新建状态,如果不是则抛异常
IllegalThreadStateException-线程状态不合法异常
- 并且后续会调用一个native start0()本地方法,本地方法是底层方法,Oracle JDK源码隐藏了native的具体实现,通过Open JDK的jdk源码和hotspot源码,查看native start0的底层源码,可以看到主要由c和c++实现的,start0会通过
JVM_StartThread
开启线程,并new JavaThread
一个native_thread本地线程,然后设置线程状态为RUNNABLE
运行状态,最后通过一个notify()
方法实现主线程调用Thread#run()
方法,程序结束后,线程状态会变成终止状态
- 因此实际开发当中,应该规范的通过线程调用start()方法来开启线程。
❤️原创不易,觉得有用的小伙伴点亮(点赞+收藏⭐),支持一下吧!❤️
给大家的福利
零基础入门
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
同时每个成长路线对应的板块都有配套的视频提供:
因篇幅有限,仅展示部分资料
网络安全面试题
绿盟护网行动
还有大家最喜欢的黑客技术
网络安全源码合集+工具包
所有资料共282G,朋友们如果有需要全套《网络安全入门+黑客进阶学习资源包》,可以扫描下方二维码领取(如遇扫码问题,可以在评论区留言领取哦)~
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!