一、线程的两种创建方式
- 继承Thread类:通过new MyThread();无参构造方法创建对象;【MyThread是Thread类的继承类】
- 实现Runnable接口:通过new Thread(new MyThread());有参构造方法创建对象;【MyThread是Runnable接口的实现类】
二、Thread源码创建Thread对象流程
- 无参构造方法和有参构造方法:两个构造方法都是调用init()方法【init有很多重载方法】
- 查看第一步中的nextThreadNum()方法:为没有名字的线程自动编码,并且加锁,保证默认生成的名字不会重复。【名字是可以重复的,id不能重复】
- 查看第一步中init()方法:
- group:指定当前线程的线程组,未指定时线程组为创建该线程所属的线程组。【通常为null】
- target:指定运行其中的Runnable。【当是无参构造方法时,为null】
- name:线程的名称,不指定自动生成。【默认为"Thread-n"】
- stackSize:预期堆栈大小,不指定默认为0,0代表忽略这个属性。【与平台相关,一般不用】
- 查看上一步中的init()方法:
- AccessControlContext:该值为null,表示用当前调用堆栈快照(AccessControlContext)初始化线程。
- inheritThreadLoclas:该值为true,表示从构建线程继承可继承的线程局部变量初始值。
- 查看上一步中的init()方法:这是主要的init()方法,Thread的所有构造方法都会调用该方法。【部分重要代码】
// 最主要的辅助构造函数,所有的构造函数均调用init函数
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
this.name = name;
Thread parent = currentThread();//当前线程为父线程
this.group = g;
//子线程继承父线程的守护属性
this.daemon = parent.isDaemon();
//子线程继承父线程的优先级
this.priority = parent.getPriority();
//当通过Runnable接口创建线程时,target就是用来保存传入的Runnable实现类的对象
//【Thread类会在重写的run方法中调用target对象的run方法】
//当通过Thread实现类创建线程时,target为null,因为Thread类的run方法以及被重写
this.target = target;
setPriority(priority);
this.stackSize = stackSize;
//获取唯一的线程id,此方法为static synchronized,保证了一个每个线程的id一定不一样
tid = nextThreadID();
}
完整代码见附件:
三、Thread源码start()之后回调run()流程
- start()方法源码:该方法调用了native的start0()方法
- Thread类要调用本地方法,首先要向jvm注册,所以Thread类的开头便是:
- 本地方法 start0定义在src\java.base\share\native\libjava\Thread.c文件中,定义了各个操作系统都要用到的关于线程的公用数据和操作:从methods[]中可以看出start0调用了JVM_StartThread方法
- 在src\hotspot\share\prims\jvm.cpp文件中有如下代码段:这里JVM_ENTRY是一个宏,用来定义JVM_StartThread函数,可以看到函数内通过JavaThread()创建了真正与平台相关的本地线程。
- 函数JavaThread()定义在src\hotspot\share\runtime\thread.cpp文件中:JavaThread()方法通过平台API创建线程
- 函数*thread_entry也在src\hotspot\share\prims\jvm.cpp文件中定义,代码如下:可以看见该函数调用了vmSymbols::run_method_name()。【就是这个调用了run方法】
- 函数run_method_name()定义src\hotspot\share\classfile\vmSymbols.hpp文件中:对应Thread或Thread实现类中的run方法。
- Java 线程调用关系图:Java线程的start方法会创建一个本地线程(通过调用JVM_StartThread函数中的JavaThread()),该线程的线程函数是定义在jvm.cpp中的JavaThread(),由thread_entry再进一步回调run方法。