Java多线程 - 线程的生命周期
引言:
Java线程被创建之后,并非是一启动就开始执行,也不是一直处于执行状态。
在线程的生命周期中,需要经过新建、就绪、运行、阻塞和死亡五种状态。
线程启动后,不可能一直占用着CPU独自运行,CPU需要再多条线程之间切换,于是线程状态也会多次在运行和就绪之间切换。
一、新建和就绪状态
当程序使用new
关键字创建了一个线程之后,这个线程就会一直处于新建状态,此时它和Java对象一样,仅仅由Java虚拟机为其分配内存,并且初始化其成员变量的值,此时线程对象没有任何线程的动态特征,程序也不会执行线程的线程执行体。
当线程对象调用了start()
方法之后,该线程就会处于就绪状态,Java虚拟机会为其创建方法调用栈和程序计数器。处于这个状态的线程并没有开始运行,只是表示该线程可以运行了,至于该线程何时开始运行,这个取决于JVM里线程调度器的调度
注意:启动线程需要使用的是start()
方法,而不是run()
方法,永远不许调用线程对象的run()
方法!
原因: 调用start()
方法,来启动线程,会把该run()
方法当成线程执行体来处理,但是如果说你要是直接调用了线程对象的run()
方法,则run()
方法立即就会别自行,而且,在run()
方法之前的其他线程无法并发执行。也就是说:如果直接调用线程对象的run()
方法,系统会把线程对象当做一个普通对象来进行处理,且run()
方法也只是一个普通的方法了,并非是一个线程执行体。
class demo {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
thread1.run();
MyThread thread2 = new MyThread();
thread2.start();
}
}
class MyThread extends Thread{
@Override
public void run(){
System.out.println(Thread.currentThread().getName());
}
}
/*
运行结果:
main
Thread-1
由此可知,如果调用run方法,则知识当成一个普通的方法运行,其线程是main线程
*/
注意:只能对处于新建状态的线程调用start()
方法,否则将会引起异常IllegalThreadStateException
小知识: 如果你想让你创建的进程立刻开始运行,你可以使用Thread.sleep(1)
来让当前运行的线程(主线程)睡眠1ms,1ms足够了,因为在这1ms内CPU并不会空闲,它回去执行另一个处于就绪状态的线程,这样子线程就开始立即执行。
二、运行和阻塞状态
如果处于就绪状态的线程获得了CPU, 开始执行run()
方法的线程执行体,则该线程处于运行状态,如果说,计算机只有一个CPU,那么在任何时刻只有一个线程处于运行状态,当然,如果是一个多处理器的机器上,将会有多个线程并行,(注意:并行:同一时刻,有多条指令在多个处理器上同时执行。)当然,当线程数大于处理器数时,依然会有多个线程在同一CPU上轮换的现象。
当一个线程开始运行之后,不存在一直处于运行状态(当然,如果说线程执行体太短,瞬间完成, 那当我没说),线程在运行的过程中需要被中断,目的是为了使得别的线程获得一个执行的机会,线程调度的具体细节取决于底层平台所采用的的侧率。
目前,现代桌面和服务器操作系统均采用抢占式调度策略,但是有一些小型设备如同手机,可能采用协作式调度策略。
线程进入阻塞状态的可能原因:
- 线程调用
sleep()
方法主动放弃所占用的处理器资源 - 线程调用了一个阻塞式IO方法,在该方法返回之前,该线程会一直被阻塞
- 线程试图获得一个同步监视器,但是该同步监视器别的线程所拥有着
- 线程在等待某个通知
- 程序调用了线程的
suspend()
方法将该线程挂起(注意:该方法容易导致死锁)
当线程正在被执行的时候阻塞了,其他的线程就可以获得执行的机会,被阻塞的线程会在何时的时候重新进入就绪状态。
针对上面导致线程进入阻塞状态的可能原因,可以分析出,发生以下情况,则会解除上面的阻塞:
- 调用
sleep()
方法的线程已经过了指定时间了 - 线程调用的阻塞式IO方法已经返回
- 线程成功地获得试图取得的同步监视器
- 线程正在等待某个通知时,其他线程发出了一个通知
- 处于挂起状态的线程调用了
resume()
修复方法
三、线程死亡
线程也会执行结束,当它执行结束的之后,就会处于死亡状态。
线程会以如下的方式结束:
run()
或call()
方法执行完成,线程正常结束- 线程跑出一个未捕获的
Exception
或者Error
- 直接调用该线程的
stop()
方法来结束该线程 – 该方法容易导致死锁,不推荐使用
注意:
- 当主线程结束的时候,其他的线程不会受到任何影响,且不会随之结束,一旦子线程启动起来之后,他就会有与主线程一样的地位,不会受到主线程的影响
- 不要试图对一个已经死亡的线程调用
start()
方法使它重新启动,死亡就是死亡了,就像恋爱被甩了,你还打算舔狗复合?!该线程已经不可以再次作为线程执行。不仅不要对死亡状态的线程调用start()
方法,且程序只能够对新建的线程调用start()
方法,当然,对新建状态的线程调用两次start()
方法也是错误的,会引发IllegalThreadState
异常。 - 判断线程死亡:可以调用线程对象的
isAlive()
方法,当线程处于就绪,运行,阻塞三种状态的时候,返回true
,如果处于新建和死亡两种状态,该方法就会返回false