仔细琢磨 start 方法,我们可以得到很多结论
查看 Thread 中start 方法的源码
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
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 */
}
}
}
1. 线程无法被启动两次,有一个线程状态值 threadStatus 记录了线程状态(猜测应该是在start0()方法中赋值),如果重复启动,会抛出 IllegalThreadStateException
2. 我们无法得知线程启动过程中做了什么,因为主要逻辑在本地方法 start0 当中,但不难知道,真正启动一个操作系统内核级线程并执行run方法中的任务也是在 start0 方法中进行的,java 线程是基于操作系统内核级线程进行映射的,所以最终需要借助于操作系统。
Java线程和操作系统线程的关系可参考https://blog.csdn.net/CringKong/article/details/79994511
3. 每个线程都有自己的私有堆栈,异常不能跨线程传播。
举个栗子,a线程中启动b线程b.start(),注意b线程真正开始执行是在start0()方法中,所以前面抛出的IllegalThreadStateException 属于a线程中抛出的异常。假如在执行start0方法时抛出运行时异常(意味着b线程启动失败),不进行catch操作,不处理异常信息,会被沿着调用堆栈(a线程的堆栈)上抛。同时finally语句块中保证了在start0出现异常,b线程启动失败后做相应的逻辑处理。
假如start0方法正常执行完毕,b线程成功启动,那么a,b线程将会在各自的堆栈中并发执行,互不影响。b线程在执行任务(执行run方法内容)过程中,假如出现了异常,它会沿着b线程的调用堆栈传递上抛,直至被 try catch捕捉,即使没有捕捉,它也不会影响a线程的执行。
4. Thread是Java语言本身对线程的抽象,也就是说在Java中,线程只有一种形式,那就是Thread的实例形式存在。
有一个经常被问的问题,创建线程有几种方式?我觉得这个问题其实不好,创建一个Thread的实例对象只有一个途径,那就是借助于new。而我们更多的是需要关注封装任务的方式,如何改变这个run方法为你需要的任务代码?
观察run方法的源码,可以发现我们其实只有两种方式,
1. 继承Thread,重新run方法
2. 通过构造方法,透传 Runnable 实例对象,对target进行赋值
单论创建线程,跟Runnable没必然关系,不用Runnable也能创建线程,任务封装才会涉及到Runnable。
3. 其实还有一种间接利用Runnable封装任务的方式,实现Callable接口,并且借助于FutureTask(关于这个,另做记录)
总结为