在三个月前学习JavaSE的时候,曾经写过有关Java多线程基础方面的博文:
最近在强化深入理解Java并发编程。以后的博文都会有一定的深度的。上面的基础部分不会过于强调。
理解线程的生命周期是深入学习Java并发编程必备的认知,这部分的内容并不难,重要的是建立一种概念体系。
首先我们创建一个线程的实例,然后调用start方法,这个时候线程会进入可执行状态,
当该线程被调度的时候,会进入运行状态,在运行的过程可能会遇到增强锁或者sleep等情况,这种情况会导致线程会进入blocked的状态,值得注意的是当这种状态结束之后,线程不能直接进入运行状态,而应该到可执行状态,等待被调度。在线程运行的时候,CPU可能会把执行权交给别的线程,这个时候也要线程也要回到可执行状态。
我们的线程在正常执行完成之后生就被终结了,当然我们在blocked阶段被打断了,这个时候线程也可能被终结了,在runnable阶段如果遇到意外,也可能会被终结。
从整个生命周期来看,start()是线程的入口,关于start(),参考最权威的说法:
线程执行时,Java虚拟机会调用run方法。这其中有两个线程在并发运行,一个是调用start方法的线程,一个是有run方法的线程。
我们看一下start方法的源码(整体的源码以后再说)
// 启动线程,线程状态从NEW进入RUNNABLE
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();
//这步解释了为什么new出来的线程只有一个能start
/*
* 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
}
}
}
这里面调用的start0
private native void start0();
是一个native方法(底层),应该是C/C++编写的。不知道你有没有这样的疑问,我new出来一个线程后,何不直接调用run方法呢?直接调用run方法是可以的,不过并没有开辟一个线程执行run方法,必须要经过start方法,调用底层代码实现创建一个线程。
不看源码也能够猜到,这里应该使用了模板方法的设计模式。
总结一下吧:
Java应用程序的main函数是一个线程,是被JVM启动的时候调用,线程的名字叫main
实现一个线程,必须创建Thread实例,override run方法,并且调用start方法
.在JVM启动后,实际上有多个线程,但是至少有一个非守护线程
当你调用一个线程start方法的时候,此时至少有两个线程,一个是调用你的线程,还有一个是执行run方法的线程
线程的生命周期分为new,runnable,running,block,termate.
所谓守护线程是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。