线程的生命周期如下图:
2、 新建与就绪
当程序使用new关键字创建一个线程之后,线程就处于新建状态了。此时线程只是被分配了内存资源,初始化了成员变量。
当线程对象被调用了start()方法之后,该线程就处于就绪状态了。表示这个线程可以运行但还没有运行,至于线程何时开始运行,取决于jvm的线程调度器。
有一点值得注意,启动线程应该使用start()方法,而不是run()方法。如果使用run()方法,程序会把run()方法当成一个普通方法立刻执行,而不会启动新线程。
此外,也不可以对已就绪的线程再次调用start()方法,会引发异常。
3、 阻塞与运行
当就绪的线程获得CPU资源开始执行方法体,该线程就变成运行状态。当线程在运行的过程中被中断,则线程变为阻塞状态。被阻塞的线程在合适的时候会重新进入就绪状态,然后再次获取CPU资源,进入运行状态。
以下情况将使线程进入阻塞状态:
- 线程调用sleep方法,主动放弃所占CPU资源。
- 线程掉用了一个阻塞式IO方法,在方法返回前,线程被阻塞。
- 线程试图获取一个同步锁,但是该锁被其他线程占有。
- 线程在等待通知(线程同步notify)
- 线程调用suspend方法挂起。该方法容易死锁,少用。
以下情况将解除阻塞状态,使线程重新进入就绪状态:
- sleep方法计时结束
- 阻塞式IO方法已经返回
- 获取了同步锁
- 等到了其他线程发出的通知
- 调用resume方法解除了挂起状态
此外,调用yield方法可以使运行状态的线程直接转入就绪状态。
4、 线程死亡
以下三种情况时线程将死亡。
- run()方法执行结束,线程结束。
- 线程抛出了一个未捕获的Exception或Error
- 调用了线程的stop()方法。该方法容易死锁,少用。
注意,死亡状态的线程不能再次调用start()方法来启动,否则会引发异常。
5、线程控制
5.1join线程
举个例子,线程A在执行中调用线程B的join方法,线程A将被阻塞,直到线程B执行完为止。
线程代码如下:
1 //通过实现Runnable接口创建线程类 2 3 public class ThreadTwo implements Runnable{ 4 5 private int i; 6 7 8 9 //run方法同样是线程的执行体 10 11 @Override 12 13 public void run() { 14 15 for (i = 0; i < 5; i++) { 16 17 //实现Runnable接口创建线程类时,只能使用Thread.currentThread()来获取当前线程 18 19 System.out.println(Thread.currentThread().getName()+" "+i); 20 21 } 22 23 } 24 25 }